diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c index 0b9b89937c..363b31bc48 100644 --- a/ext/wayland/gstwaylandsink.c +++ b/ext/wayland/gstwaylandsink.c @@ -333,11 +333,7 @@ gst_wayland_sink_change_state (GstElement * element, GstStateChange transition) g_clear_object (&sink->window); } else { /* remove buffer from surface, show nothing */ - wl_surface_attach (sink->window->surface, NULL, 0, 0); - wl_surface_damage (sink->window->surface, 0, 0, - sink->window->surface_width, sink->window->surface_height); - wl_surface_commit (sink->window->surface); - wl_display_flush (sink->display->display); + gst_wl_window_render (sink->window, NULL, NULL); } } break; @@ -558,6 +554,7 @@ static void render_last_buffer (GstWaylandSink * sink) { GstWlBuffer *wlbuffer; + const GstVideoInfo *info = NULL; struct wl_surface *surface; struct wl_callback *callback; @@ -568,12 +565,11 @@ render_last_buffer (GstWaylandSink * sink) callback = wl_surface_frame (surface); wl_callback_add_listener (callback, &frame_callback_listener, sink); - gst_wl_buffer_attach (wlbuffer, sink->window); - wl_surface_damage (surface, 0, 0, sink->window->surface_width, - sink->window->surface_height); - - wl_surface_commit (surface); - wl_display_flush (sink->display->display); + if (G_UNLIKELY (sink->video_info_changed)) { + info = &sink->video_info; + sink->video_info_changed = FALSE; + } + gst_wl_window_render (sink->window, wlbuffer, info); } static GstFlowReturn @@ -595,33 +591,19 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer) gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink)); g_mutex_lock (&sink->render_lock); - if (sink->window) { - /* inform the window about our caps */ - gst_wl_window_set_video_info (sink->window, &sink->video_info); - } else { + if (!sink->window) { /* if we were not provided a window, create one ourselves */ sink->window = gst_wl_window_new_toplevel (sink->display, &sink->video_info); } - sink->video_info_changed = FALSE; } /* drop buffers until we get a frame callback */ if (g_atomic_int_get (&sink->redraw_pending) == TRUE) goto done; - if (G_UNLIKELY (sink->video_info_changed)) { - gst_wl_window_set_video_info (sink->window, &sink->video_info); - sink->video_info_changed = FALSE; - } - - /* now that we have for sure set the video info on the window, it must have - * a valid size, otherwise this means that the application has called - * set_window_handle() without calling set_render_rectangle(), which is - * absolutely necessary for us. - */ - if (G_UNLIKELY (sink->window->surface_width == 0 || - sink->window->surface_height == 0)) + /* make sure that the application has called set_render_rectangle() */ + if (G_UNLIKELY (sink->window->render_rectangle.w == 0)) goto no_window_size; wlbuffer = gst_buffer_get_wl_buffer (buffer); @@ -822,14 +804,14 @@ gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video) g_return_if_fail (sink != NULL); g_mutex_lock (&sink->render_lock); - if (!sink->window || !sink->window->subsurface) { + if (!sink->window || !sink->window->area_subsurface) { g_mutex_unlock (&sink->render_lock); GST_INFO_OBJECT (sink, "begin_geometry_change called without window, ignoring"); return; } - wl_subsurface_set_sync (sink->window->subsurface); + wl_subsurface_set_sync (sink->window->area_subsurface); g_mutex_unlock (&sink->render_lock); } @@ -840,14 +822,14 @@ gst_wayland_sink_end_geometry_change (GstWaylandVideo * video) g_return_if_fail (sink != NULL); g_mutex_lock (&sink->render_lock); - if (!sink->window || !sink->window->subsurface) { + if (!sink->window || !sink->window->area_subsurface) { g_mutex_unlock (&sink->render_lock); GST_INFO_OBJECT (sink, "end_geometry_change called without window, ignoring"); return; } - wl_subsurface_set_desync (sink->window->subsurface); + wl_subsurface_set_desync (sink->window->area_subsurface); g_mutex_unlock (&sink->render_lock); } diff --git a/ext/wayland/wlbuffer.c b/ext/wayland/wlbuffer.c index c2bf3208b2..d93fedb7ff 100644 --- a/ext/wayland/wlbuffer.c +++ b/ext/wayland/wlbuffer.c @@ -120,7 +120,7 @@ static const struct wl_buffer_listener buffer_listener = { buffer_release }; -void +GstWlBuffer * gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer, GstWlDisplay * display) { @@ -137,6 +137,8 @@ gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer, gst_mini_object_set_qdata ((GstMiniObject *) gstbuffer, gst_wl_buffer_qdata_quark (), self, g_object_unref); + + return self; } GstWlBuffer * @@ -172,11 +174,11 @@ gst_wl_buffer_force_release_and_unref (GstWlBuffer * self) } void -gst_wl_buffer_attach (GstWlBuffer * self, GstWlWindow * target) +gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface) { g_return_if_fail (self->used_by_compositor == FALSE); - wl_surface_attach (target->surface, self->wlbuffer, 0, 0); + wl_surface_attach (surface, self->wlbuffer, 0, 0); /* Add a reference to the buffer. This represents the fact that * the compositor is using the buffer and it should not return diff --git a/ext/wayland/wlbuffer.h b/ext/wayland/wlbuffer.h index b434df469c..cbb50f719a 100644 --- a/ext/wayland/wlbuffer.h +++ b/ext/wayland/wlbuffer.h @@ -21,7 +21,7 @@ #ifndef __GST_WL_BUFFER_H__ #define __GST_WL_BUFFER_H__ -#include "wlwindow.h" +#include "wldisplay.h" G_BEGIN_DECLS @@ -54,13 +54,13 @@ struct _GstWlBufferClass GType gst_wl_buffer_get_type (void); -void gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, +GstWlBuffer * gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer * wlbuffer, GstWlDisplay * display); GstWlBuffer * gst_buffer_get_wl_buffer (GstBuffer * gstbuffer); void gst_wl_buffer_force_release_and_unref (GstWlBuffer * self); -void gst_wl_buffer_attach (GstWlBuffer * self, GstWlWindow * target); +void gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface); G_END_DECLS diff --git a/ext/wayland/wlvideoformat.h b/ext/wayland/wlvideoformat.h index 833b98db34..e8ec7ae129 100644 --- a/ext/wayland/wlvideoformat.h +++ b/ext/wayland/wlvideoformat.h @@ -24,7 +24,7 @@ #ifndef __GST_WL_VIDEO_FORMAT_H__ #define __GST_WL_VIDEO_FORMAT_H__ -#include +#include #include G_BEGIN_DECLS diff --git a/ext/wayland/wlwindow.c b/ext/wayland/wlwindow.c index f58df09654..a4da2c6624 100644 --- a/ext/wayland/wlwindow.c +++ b/ext/wayland/wlwindow.c @@ -25,6 +25,8 @@ #endif #include "wlwindow.h" +#include "wlshmallocator.h" +#include "wlbuffer.h" GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); #define GST_CAT_DEFAULT gstwayland_debug @@ -78,12 +80,15 @@ gst_wl_window_finalize (GObject * gobject) wl_shell_surface_destroy (self->shell_surface); } - if (self->subsurface) { - wl_subsurface_destroy (self->subsurface); - } + wl_viewport_destroy (self->video_viewport); + wl_subsurface_destroy (self->video_subsurface); + wl_surface_destroy (self->video_surface); - wl_viewport_destroy (self->viewport); - wl_surface_destroy (self->surface); + if (self->area_subsurface) { + wl_subsurface_destroy (self->area_subsurface); + } + wl_viewport_destroy (self->area_viewport); + wl_surface_destroy (self->area_surface); g_clear_object (&self->display); @@ -91,44 +96,84 @@ gst_wl_window_finalize (GObject * gobject) } static GstWlWindow * -gst_wl_window_new_internal (GstWlDisplay * display, struct wl_surface *surface) +gst_wl_window_new_internal (GstWlDisplay * display) { GstWlWindow *window; + GstVideoInfo info; + GstBuffer *buf; + GstMapInfo mapinfo; + struct wl_buffer *wlbuf; + GstWlBuffer *gwlbuf; struct wl_region *region; - g_return_val_if_fail (surface != NULL, NULL); - window = g_object_new (GST_TYPE_WL_WINDOW, NULL); window->display = g_object_ref (display); - window->surface = surface; - /* make sure the surface runs on our local queue */ - wl_proxy_set_queue ((struct wl_proxy *) surface, display->queue); + window->area_surface = wl_compositor_create_surface (display->compositor); + window->video_surface = wl_compositor_create_surface (display->compositor); - window->viewport = wl_scaler_get_viewport (display->scaler, window->surface); + wl_proxy_set_queue ((struct wl_proxy *) window->area_surface, display->queue); + wl_proxy_set_queue ((struct wl_proxy *) window->video_surface, + display->queue); + + /* embed video_surface in area_surface */ + window->video_subsurface = + wl_subcompositor_get_subsurface (display->subcompositor, + window->video_surface, window->area_surface); + wl_subsurface_set_desync (window->video_subsurface); + + window->area_viewport = wl_scaler_get_viewport (display->scaler, + window->area_surface); + window->video_viewport = wl_scaler_get_viewport (display->scaler, + window->video_surface); + + /* draw the area_subsurface */ + gst_video_info_set_format (&info, + /* we want WL_SHM_FORMAT_XRGB8888 */ +#if G_BYTE_ORDER == G_BIG_ENDIAN + GST_VIDEO_FORMAT_xRGB, +#else + GST_VIDEO_FORMAT_BGRx, +#endif + 1, 1); + + buf = gst_buffer_new_allocate (gst_wl_shm_allocator_get (), info.size, NULL); + gst_buffer_map (buf, &mapinfo, GST_MAP_WRITE); + *((guint32 *) mapinfo.data) = 0; /* paint it black */ + gst_buffer_unmap (buf, &mapinfo); + wlbuf = + gst_wl_shm_memory_construct_wl_buffer (gst_buffer_peek_memory (buf, 0), + display, &info); + gwlbuf = gst_buffer_add_wl_buffer (buf, wlbuf, display); + gst_wl_buffer_attach (gwlbuf, window->area_surface); + + /* at this point, the GstWlBuffer keeps the buffer + * alive and will free it on wl_buffer::release */ + gst_buffer_unref (buf); /* do not accept input */ region = wl_compositor_create_region (display->compositor); - wl_surface_set_input_region (surface, region); + wl_surface_set_input_region (window->area_surface, region); + wl_region_destroy (region); + + region = wl_compositor_create_region (display->compositor); + wl_surface_set_input_region (window->video_surface, region); wl_region_destroy (region); return window; } GstWlWindow * -gst_wl_window_new_toplevel (GstWlDisplay * display, GstVideoInfo * video_info) +gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info) { GstWlWindow *window; + gint width; - window = gst_wl_window_new_internal (display, - wl_compositor_create_surface (display->compositor)); - - gst_wl_window_set_video_info (window, video_info); - gst_wl_window_set_render_rectangle (window, 0, 0, window->video_width, - window->video_height); + window = gst_wl_window_new_internal (display); + /* go toplevel */ window->shell_surface = wl_shell_get_shell_surface (display->shell, - window->surface); + window->area_surface); if (window->shell_surface) { wl_shell_surface_add_listener (window->shell_surface, @@ -141,6 +186,11 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, GstVideoInfo * video_info) return NULL; } + /* set the initial size to be the same as the reported video size */ + width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height); + return window; } @@ -149,13 +199,13 @@ gst_wl_window_new_in_surface (GstWlDisplay * display, struct wl_surface * parent) { GstWlWindow *window; + window = gst_wl_window_new_internal (display); - window = gst_wl_window_new_internal (display, - wl_compositor_create_surface (display->compositor)); - - window->subsurface = wl_subcompositor_get_subsurface (display->subcompositor, - window->surface, parent); - wl_subsurface_set_desync (window->subsurface); + /* embed in parent */ + window->area_subsurface = + wl_subcompositor_get_subsurface (display->subcompositor, + window->area_surface, parent); + wl_subsurface_set_desync (window->area_subsurface); return window; } @@ -173,7 +223,7 @@ gst_wl_window_get_wl_surface (GstWlWindow * window) { g_return_val_if_fail (window != NULL, NULL); - return window->surface; + return window->video_surface; } gboolean @@ -185,22 +235,21 @@ gst_wl_window_is_toplevel (GstWlWindow * window) } static void -gst_wl_window_resize_internal (GstWlWindow * window, gboolean commit) +gst_wl_window_resize_video_surface (GstWlWindow * window, gboolean commit) { GstVideoRectangle src, res; + /* center the video_subsurface inside area_subsurface */ src.w = window->video_width; src.h = window->video_height; gst_video_sink_center_rect (src, window->render_rectangle, &res, TRUE); - if (window->subsurface) - wl_subsurface_set_position (window->subsurface, - window->render_rectangle.x + res.x, window->render_rectangle.y + res.y); - wl_viewport_set_destination (window->viewport, res.w, res.h); + wl_subsurface_set_position (window->video_subsurface, res.x, res.y); + wl_viewport_set_destination (window->video_viewport, res.w, res.h); if (commit) { - wl_surface_damage (window->surface, 0, 0, res.w, res.h); - wl_surface_commit (window->surface); + wl_surface_damage (window->video_surface, 0, 0, res.w, res.h); + wl_surface_commit (window->video_surface); } /* this is saved for use in wl_surface_damage */ @@ -209,16 +258,37 @@ gst_wl_window_resize_internal (GstWlWindow * window, gboolean commit) } void -gst_wl_window_set_video_info (GstWlWindow * window, GstVideoInfo * info) +gst_wl_window_render (GstWlWindow * window, GstWlBuffer * buffer, + const GstVideoInfo * info) { - g_return_if_fail (window != NULL); + if (G_UNLIKELY (info)) { + window->video_width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + window->video_height = info->height; - window->video_width = - gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); - window->video_height = info->height; + wl_subsurface_set_sync (window->video_subsurface); + gst_wl_window_resize_video_surface (window, FALSE); + } - if (window->render_rectangle.w != 0) - gst_wl_window_resize_internal (window, FALSE); + if (G_LIKELY (buffer)) + gst_wl_buffer_attach (buffer, window->video_surface); + else + wl_surface_attach (window->video_surface, NULL, 0, 0); + + wl_surface_damage (window->video_surface, 0, 0, window->surface_width, + window->surface_height); + wl_surface_commit (window->video_surface); + + if (G_UNLIKELY (info)) { + /* commit also the parent (area_surface) in order to change + * the position of the video_subsurface */ + wl_surface_damage (window->area_surface, 0, 0, window->render_rectangle.w, + window->render_rectangle.h); + wl_surface_commit (window->area_surface); + wl_subsurface_set_desync (window->video_subsurface); + } + + wl_display_flush (window->display->display); } void @@ -232,6 +302,21 @@ gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, window->render_rectangle.w = w; window->render_rectangle.h = h; + /* position the area inside the parent - needs a parent commit to apply */ + if (window->area_subsurface) + wl_subsurface_set_position (window->area_subsurface, x, y); + + /* change the size of the area */ + wl_viewport_set_destination (window->area_viewport, w, h); + + if (window->video_width != 0) { + wl_subsurface_set_sync (window->video_subsurface); + gst_wl_window_resize_video_surface (window, TRUE); + } + + wl_surface_damage (window->area_surface, 0, 0, w, h); + wl_surface_commit (window->area_surface); + if (window->video_width != 0) - gst_wl_window_resize_internal (window, TRUE); + wl_subsurface_set_desync (window->video_subsurface); } diff --git a/ext/wayland/wlwindow.h b/ext/wayland/wlwindow.h index f4cb36083f..e22cb261b5 100644 --- a/ext/wayland/wlwindow.h +++ b/ext/wayland/wlwindow.h @@ -22,6 +22,7 @@ #define __GST_WL_WINDOW_H__ #include "wldisplay.h" +#include "wlbuffer.h" #include G_BEGIN_DECLS @@ -41,16 +42,19 @@ struct _GstWlWindow GObject parent_instance; GstWlDisplay *display; - struct wl_surface *surface; - struct wl_subsurface *subsurface; - struct wl_viewport *viewport; + struct wl_surface *area_surface; + struct wl_subsurface *area_subsurface; + struct wl_viewport *area_viewport; + struct wl_surface *video_surface; + struct wl_subsurface *video_subsurface; + struct wl_viewport *video_viewport; struct wl_shell_surface *shell_surface; - /* the size of the destination area where we are overlaying our subsurface */ + /* the size and position of the area_(sub)surface */ GstVideoRectangle render_rectangle; /* the size of the video in the buffers */ gint video_width, video_height; - /* the size of the (sub)surface */ + /* the size of the video_(sub)surface */ gint surface_width, surface_height; }; @@ -62,7 +66,7 @@ struct _GstWlWindowClass GType gst_wl_window_get_type (void); GstWlWindow *gst_wl_window_new_toplevel (GstWlDisplay * display, - GstVideoInfo * video_info); + const GstVideoInfo * info); GstWlWindow *gst_wl_window_new_in_surface (GstWlDisplay * display, struct wl_surface * parent); @@ -70,8 +74,8 @@ GstWlDisplay *gst_wl_window_get_display (GstWlWindow * window); struct wl_surface *gst_wl_window_get_wl_surface (GstWlWindow * window); gboolean gst_wl_window_is_toplevel (GstWlWindow *window); -/* functions to manipulate the size on non-toplevel windows */ -void gst_wl_window_set_video_info (GstWlWindow * window, GstVideoInfo * info); +void gst_wl_window_render (GstWlWindow * window, GstWlBuffer * buffer, + const GstVideoInfo * info); void gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, gint w, gint h);