3 * @brief Wayland shared memory video output module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2014, 2017 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>
36 #include <wayland-client.h>
37 #include "viewporter-client-protocol.h"
39 #include <vlc_common.h>
40 #include <vlc_plugin.h>
41 #include <vlc_vout_display.h>
42 #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 wp_viewporter
*viewporter
;
53 struct wp_viewport
*viewport
;
55 picture_pool_t
*pool
; /* picture pool */
59 unsigned display_width
;
60 unsigned display_height
;
61 bool use_buffer_transform
;
64 static void PictureDestroy(picture_t
*pic
)
66 const long pagemask
= sysconf(_SC_PAGE_SIZE
) - 1;
67 size_t picsize
= pic
->p
[0].i_pitch
* pic
->p
[0].i_lines
;
69 munmap(pic
->p
[0].p_pixels
, (picsize
+ pagemask
) & ~pagemask
);
73 static void buffer_release_cb(void *data
, struct wl_buffer
*buffer
)
75 picture_t
*pic
= data
;
78 wl_buffer_set_user_data(buffer
, NULL
);
82 static const struct wl_buffer_listener buffer_cbs
=
87 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned req
)
89 vout_display_sys_t
*sys
= vd
->sys
;
91 if (sys
->pool
!= NULL
)
94 if (req
> MAX_PICTURES
)
100 msg_Err(vd
, "cannot create buffers: %s", vlc_strerror_c(errno
));
104 /* We need one extra line to cover for horizontal crop offset */
105 unsigned stride
= 4 * ((vd
->fmt
.i_width
+ 31) & ~31);
106 unsigned lines
= (vd
->fmt
.i_height
+ 31 + (sys
->viewport
== NULL
)) & ~31;
107 const long pagemask
= sysconf(_SC_PAGE_SIZE
) - 1;
108 size_t picsize
= ((stride
* lines
) + pagemask
) & ~pagemask
;
109 size_t length
= picsize
* req
;
111 if (ftruncate(fd
, length
))
113 msg_Err(vd
, "cannot allocate buffers: %s", vlc_strerror_c(errno
));
118 void *base
= mmap(NULL
, length
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
119 if (base
== MAP_FAILED
)
121 msg_Err(vd
, "cannot map buffers: %s", vlc_strerror_c(errno
));
126 memset(base
, 0x80, length
); /* gray fill */
129 struct wl_shm_pool
*shm_pool
= wl_shm_create_pool(sys
->shm
, fd
, length
);
131 if (shm_pool
== NULL
)
133 munmap(base
, length
);
137 picture_t
*pics
[MAX_PICTURES
];
138 picture_resource_t res
= {
139 .pf_destroy
= PictureDestroy
,
148 unsigned width
= vd
->fmt
.i_visible_width
;
149 unsigned height
= vd
->fmt
.i_visible_height
;
152 if (sys
->viewport
== NULL
) /* Poor man's crop */
153 offset
+= 4 * vd
->fmt
.i_x_offset
+ stride
* vd
->fmt
.i_y_offset
;
157 struct wl_buffer
*buf
;
159 buf
= wl_shm_pool_create_buffer(shm_pool
, offset
, width
, height
,
160 stride
, WL_SHM_FORMAT_XRGB8888
);
165 res
.p
[0].p_pixels
= base
;
166 base
= ((char *)base
) + picsize
;
170 picture_t
*pic
= picture_NewFromResource(&vd
->fmt
, &res
);
171 if (unlikely(pic
== NULL
))
173 wl_buffer_destroy(buf
);
177 wl_buffer_add_listener(buf
, &buffer_cbs
, NULL
);
181 wl_shm_pool_destroy(shm_pool
);
182 wl_display_flush(sys
->embed
->display
.wl
);
185 munmap(base
, length
); /* Left-over buffers */
189 sys
->pool
= picture_pool_New (count
, pics
);
190 if (unlikely(sys
->pool
== NULL
))
193 picture_Release(pics
[--count
]);
198 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
;
209 wl_buffer_set_user_data(buf
, pic
);
210 wl_surface_attach(surface
, buf
, 0, 0);
211 wl_surface_damage(surface
, 0, 0, sys
->display_width
, sys
->display_height
);
212 wl_display_flush(display
);
220 static void Display(vout_display_t
*vd
, picture_t
*pic
)
222 vout_display_sys_t
*sys
= vd
->sys
;
223 struct wl_display
*display
= sys
->embed
->display
.wl
;
224 struct wl_surface
*surface
= sys
->embed
->handle
.wl
;
226 wl_surface_commit(surface
);
227 wl_display_roundtrip_queue(display
, sys
->eventq
);
232 static void PictureCheckAttached(void *data
, picture_t
*pic
)
234 unsigned *countp
= data
;
236 (*countp
) += wl_buffer_get_user_data(pic
->p_sys
) != NULL
;
239 static unsigned CountActiveBuffers(picture_pool_t
*pool
)
243 picture_pool_Enum(pool
, PictureCheckAttached
, &count
);
247 static void PictureBufferDestroy(void *data
, picture_t
*pic
)
249 struct wl_buffer
*buf
= (struct wl_buffer
*)pic
->p_sys
;
251 wl_buffer_destroy(buf
);
255 static void ResetPictures(vout_display_t
*vd
)
257 vout_display_sys_t
*sys
= vd
->sys
;
258 struct wl_display
*display
= sys
->embed
->display
.wl
;
259 struct wl_surface
*surface
= sys
->embed
->handle
.wl
;
261 if (sys
->pool
== NULL
)
264 wl_surface_attach(surface
, NULL
, 0, 0);
265 wl_surface_commit(surface
);
267 /* Wait until all picture buffers are released by the server */
268 while (CountActiveBuffers(sys
->pool
) > 0)
269 wl_display_roundtrip_queue(display
, sys
->eventq
);
271 /* Destroy the buffers */
272 picture_pool_Enum(sys
->pool
, PictureBufferDestroy
, NULL
);
273 picture_pool_Release(sys
->pool
);
277 static int Control(vout_display_t
*vd
, int query
, va_list ap
)
279 vout_display_sys_t
*sys
= vd
->sys
;
283 case VOUT_DISPLAY_RESET_PICTURES
:
285 const vout_display_cfg_t
*cfg
= va_arg(ap
, const vout_display_cfg_t
*);
286 video_format_t
*fmt
= va_arg(ap
, video_format_t
*);
287 vout_display_place_t place
;
289 assert(sys
->viewport
== NULL
);
291 vout_display_PlacePicture(&place
, &vd
->source
, cfg
, false);
292 video_format_ApplyRotation(&src
, &vd
->source
);
294 fmt
->i_width
= src
.i_width
* place
.width
295 / src
.i_visible_width
;
296 fmt
->i_height
= src
.i_height
* place
.height
297 / src
.i_visible_height
;
298 fmt
->i_visible_width
= place
.width
;
299 fmt
->i_visible_height
= place
.height
;
300 fmt
->i_x_offset
= src
.i_x_offset
* place
.width
301 / src
.i_visible_width
;
302 fmt
->i_y_offset
= src
.i_y_offset
* place
.height
303 / src
.i_visible_height
;
309 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
:
310 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
311 case VOUT_DISPLAY_CHANGE_ZOOM
:
312 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
:
313 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
315 const vout_display_cfg_t
*cfg
= va_arg(ap
, const vout_display_cfg_t
*);
316 sys
->display_width
= cfg
->display
.width
;
317 sys
->display_height
= cfg
->display
.height
;
319 if (sys
->viewport
!= NULL
)
322 vout_display_place_t place
;
324 video_format_ApplyRotation(&fmt
, &vd
->source
);
325 vout_display_PlacePicture(&place
, &vd
->source
, cfg
, false);
327 wp_viewport_set_source(sys
->viewport
,
328 wl_fixed_from_int(fmt
.i_x_offset
),
329 wl_fixed_from_int(fmt
.i_y_offset
),
330 wl_fixed_from_int(fmt
.i_visible_width
),
331 wl_fixed_from_int(fmt
.i_visible_height
));
332 wp_viewport_set_destination(sys
->viewport
,
333 place
.width
, place
.height
);
336 vout_display_SendEventPicturesInvalid(vd
);
340 msg_Err(vd
, "unknown request %d", query
);
346 static void shm_format_cb(void *data
, struct wl_shm
*shm
, uint32_t format
)
348 vout_display_t
*vd
= data
;
351 memcpy(str
, &format
, sizeof (str
));
353 if (format
>= 0x20202020)
354 msg_Dbg(vd
, "format %.4s (0x%08"PRIx32
")", str
, format
);
356 msg_Dbg(vd
, "format %4"PRIu32
" (0x%08"PRIx32
")", format
, format
);
360 static const struct wl_shm_listener shm_cbs
=
365 static void registry_global_cb(void *data
, struct wl_registry
*registry
,
366 uint32_t name
, const char *iface
, uint32_t vers
)
368 vout_display_t
*vd
= data
;
369 vout_display_sys_t
*sys
= vd
->sys
;
371 msg_Dbg(vd
, "global %3"PRIu32
": %s version %"PRIu32
, name
, iface
, vers
);
373 if (!strcmp(iface
, "wl_shm"))
374 sys
->shm
= wl_registry_bind(registry
, name
, &wl_shm_interface
, 1);
376 if (!strcmp(iface
, "wp_viewporter"))
377 sys
->viewporter
= wl_registry_bind(registry
, name
,
378 &wp_viewporter_interface
, 1);
380 if (!strcmp(iface
, "wl_compositor"))
381 sys
->use_buffer_transform
= vers
>= 2;
384 static void registry_global_remove_cb(void *data
, struct wl_registry
*registry
,
387 vout_display_t
*vd
= data
;
389 msg_Dbg(vd
, "global remove %3"PRIu32
, name
);
393 static const struct wl_registry_listener registry_cbs
=
396 registry_global_remove_cb
,
399 static int Open(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
400 video_format_t
*fmtp
, vlc_video_context
*context
)
402 if (cfg
->window
->type
!= VOUT_WINDOW_TYPE_WAYLAND
)
405 vout_display_sys_t
*sys
= malloc(sizeof (*sys
));
406 if (unlikely(sys
== NULL
))
413 sys
->viewporter
= NULL
;
417 sys
->display_width
= cfg
->display
.width
;
418 sys
->display_height
= cfg
->display
.height
;
419 sys
->use_buffer_transform
= false;
422 sys
->embed
= cfg
->window
;
423 assert(sys
->embed
!= NULL
);
425 struct wl_display
*display
= sys
->embed
->display
.wl
;
427 sys
->eventq
= wl_display_create_queue(display
);
428 if (sys
->eventq
== NULL
)
431 struct wl_registry
*registry
= wl_display_get_registry(display
);
432 if (registry
== NULL
)
435 wl_proxy_set_queue((struct wl_proxy
*)registry
, sys
->eventq
);
436 wl_registry_add_listener(registry
, ®istry_cbs
, vd
);
437 wl_display_roundtrip_queue(display
, sys
->eventq
);
438 wl_registry_destroy(registry
);
440 if (sys
->shm
== NULL
)
443 wl_shm_add_listener(sys
->shm
, &shm_cbs
, vd
);
444 wl_display_roundtrip_queue(display
, sys
->eventq
);
446 struct wl_surface
*surface
= sys
->embed
->handle
.wl
;
447 if (sys
->viewporter
!= NULL
)
448 sys
->viewport
= wp_viewporter_get_viewport(sys
->viewporter
, surface
);
450 sys
->viewport
= NULL
;
452 /* Determine our pixel format */
453 static const enum wl_output_transform transforms
[8] = {
454 [ORIENT_TOP_LEFT
] = WL_OUTPUT_TRANSFORM_NORMAL
,
455 [ORIENT_TOP_RIGHT
] = WL_OUTPUT_TRANSFORM_FLIPPED
,
456 [ORIENT_BOTTOM_LEFT
] = WL_OUTPUT_TRANSFORM_FLIPPED_180
,
457 [ORIENT_BOTTOM_RIGHT
] = WL_OUTPUT_TRANSFORM_180
,
458 [ORIENT_LEFT_TOP
] = WL_OUTPUT_TRANSFORM_FLIPPED_270
,
459 [ORIENT_LEFT_BOTTOM
] = WL_OUTPUT_TRANSFORM_90
,
460 [ORIENT_RIGHT_TOP
] = WL_OUTPUT_TRANSFORM_270
,
461 [ORIENT_RIGHT_BOTTOM
] = WL_OUTPUT_TRANSFORM_FLIPPED_90
,
464 if (sys
->use_buffer_transform
)
466 wl_surface_set_buffer_transform(surface
,
467 transforms
[fmtp
->orientation
]);
471 video_format_t fmt
= *fmtp
;
472 video_format_ApplyRotation(fmtp
, &fmt
);
475 fmtp
->i_chroma
= VLC_CODEC_RGB32
;
477 vd
->info
.has_pictures_invalid
= sys
->viewport
== NULL
;
480 vd
->prepare
= Prepare
;
481 vd
->display
= Display
;
482 vd
->control
= Control
;
488 if (sys
->eventq
!= NULL
)
489 wl_event_queue_destroy(sys
->eventq
);
494 static void Close(vout_display_t
*vd
)
496 vout_display_sys_t
*sys
= vd
->sys
;
500 if (sys
->viewport
!= NULL
)
501 wp_viewport_destroy(sys
->viewport
);
502 if (sys
->viewporter
!= NULL
)
503 wp_viewporter_destroy(sys
->viewporter
);
504 wl_shm_destroy(sys
->shm
);
505 wl_display_flush(sys
->embed
->display
.wl
);
506 wl_event_queue_destroy(sys
->eventq
);
511 set_shortname(N_("WL SHM"))
512 set_description(N_("Wayland shared memory video output"))
513 set_category(CAT_VIDEO
)
514 set_subcategory(SUBCAT_VIDEO_VOUT
)
515 set_capability("vout display", 170)
516 set_callbacks(Open
, Close
)