vaapisink: listen to window size changes on X11.
authorHolger Kaelberer <hk@getslash.de>
Tue, 5 Nov 2013 13:01:11 +0000 (14:01 +0100)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Wed, 30 Jul 2014 13:46:14 +0000 (15:46 +0200)
Allow dynamic changes to the window, e.g. performed by the user, and
make sure to refresh its contents, while preserving aspect ratio.

In practice, Expose and ConfigureNotify events are tracked in X11
display mode by default. This occurs in a separte event thread, and
this is similar to what xvimagesink does. Any of those events will
trigger a reconfiguration of the window "soft" size, subsequently
the render-rect when necessary, and finally _expose() the result.

The default of handle_events=true can be changed programatically via
gst_x_overlay_handle_events().

Thanks to Fabrice Bellet for rebasing the patch.

https://bugzilla.gnome.org/show_bug.cgi?id=711478

[dropped XInitThreads(), cleaned up the code a little]
Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
gst/vaapi/gstvaapisink.c
gst/vaapi/gstvaapisink.h

index 3753d77..341c23b 100644 (file)
@@ -146,14 +146,23 @@ gst_vaapisink_ensure_display(GstVaapiSink *sink);
 
 /* GstVideoOverlay interface */
 
+static void
+gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay);
+
 #if USE_X11
 static gboolean
 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
 #endif
 
+static void
+gst_vaapisink_set_event_handling(GstVideoOverlay *overlay, gboolean handle_events);
+
 static GstFlowReturn
 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
 
+static gboolean
+gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height);
+
 static void
 gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
     guintptr window)
@@ -179,6 +188,7 @@ gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
 #if USE_X11
     case GST_VAAPI_DISPLAY_TYPE_X11:
         gst_vaapisink_ensure_window_xid(sink, window);
+        gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), sink->handle_events);
         break;
 #endif
     default:
@@ -208,13 +218,175 @@ gst_vaapisink_video_overlay_set_render_rectangle(
               display_rect->width, display_rect->height);
 }
 
+static gboolean
+gst_vaapisink_reconfigure_window(GstVaapiSink * sink)
+{
+    guint win_width, win_height;
+
+    gst_vaapi_window_reconfigure(sink->window);
+    gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
+    if (win_width != sink->window_width || win_height != sink->window_height) {
+        if (!gst_vaapisink_ensure_render_rect(sink, win_width, win_height))
+            return FALSE;
+        GST_INFO("window was resized from %ux%u to %ux%u",
+            sink->window_width, sink->window_height, win_width, win_height);
+        sink->window_width = win_width;
+        sink->window_height = win_height;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+#if USE_X11
+static void
+gst_vaapisink_event_thread_loop_x11(GstVaapiSink *sink)
+{
+    GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
+    gboolean has_events, do_expose = FALSE;
+    XEvent e;
+
+    if (sink->window) {
+        Display * const x11_dpy =
+            gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(display));
+        Window x11_win =
+            gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window));
+
+        /* Handle Expose + ConfigureNotify */
+        /* Need to lock whole loop or we corrupt the XEvent queue: */
+        for (;;) {
+            gst_vaapi_display_lock(display);
+            has_events = XCheckWindowEvent(x11_dpy, x11_win,
+                StructureNotifyMask | ExposureMask, &e);
+            gst_vaapi_display_unlock(display);
+            if (!has_events)
+                break;
+
+            switch (e.type) {
+            case Expose:
+                do_expose = TRUE;
+                break;
+            case ConfigureNotify:
+                if (gst_vaapisink_reconfigure_window(sink))
+                    do_expose = TRUE;
+                break;
+            default:
+                break;
+            }
+        }
+        if (do_expose)
+            gst_vaapisink_video_overlay_expose(GST_VIDEO_OVERLAY(sink));
+        /* FIXME: handle mouse and key events */
+    }
+}
+#endif
+
+static void
+gst_vaapisink_event_thread_loop_default(GstVaapiSink *sink)
+{
+}
+
+static gpointer
+gst_vaapisink_event_thread (GstVaapiSink *sink)
+{
+    void (*thread_loop)(GstVaapiSink *sink);
+
+    switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
+#if USE_X11
+    case GST_VAAPI_DISPLAY_TYPE_X11:
+    case GST_VAAPI_DISPLAY_TYPE_GLX:
+        thread_loop = gst_vaapisink_event_thread_loop_x11;
+        break;
+#endif
+    default:
+        thread_loop = gst_vaapisink_event_thread_loop_default;
+        break;
+    }
+
+    GST_OBJECT_LOCK(sink);
+    while (!sink->event_thread_cancel) {
+        GST_OBJECT_UNLOCK(sink);
+        thread_loop(sink);
+        g_usleep(G_USEC_PER_SEC / 20);
+        GST_OBJECT_LOCK(sink);
+    }
+    GST_OBJECT_UNLOCK(sink);
+
+  return NULL;
+}
+
 static void
 gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay)
 {
     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
 
-    if (sink->video_buffer)
+    if (sink->video_buffer) {
+        gst_vaapisink_reconfigure_window(sink);
         gst_vaapisink_show_frame(GST_BASE_SINK_CAST(sink), sink->video_buffer);
+    }
+}
+
+static void
+gst_vaapisink_set_event_handling(GstVideoOverlay *overlay,
+    gboolean handle_events)
+{
+    GThread *thread = NULL;
+    GstVaapiSink * const sink = GST_VAAPISINK(overlay);
+#if USE_X11
+    GstVaapiDisplayX11 * const display =
+        GST_VAAPI_DISPLAY_X11(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
+#endif
+
+    GST_OBJECT_LOCK(sink);
+    sink->handle_events = handle_events;
+    if (handle_events && !sink->event_thread) {
+        /* Setup our event listening thread */
+        GST_DEBUG("starting xevent thread");
+        switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
+#if USE_X11
+        case GST_VAAPI_DISPLAY_TYPE_X11:
+        case GST_VAAPI_DISPLAY_TYPE_GLX:
+            XSelectInput(gst_vaapi_display_x11_get_display(display),
+                gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)),
+                StructureNotifyMask | ExposureMask);
+            break;
+#endif
+        default:
+            break;
+        }
+
+        sink->event_thread_cancel = FALSE;
+        sink->event_thread = g_thread_try_new("vaapisink-events",
+                (GThreadFunc) gst_vaapisink_event_thread, sink, NULL);
+    }
+    else if (!handle_events && sink->event_thread) {
+        GST_DEBUG("stopping xevent thread");
+        if (sink->window) {
+            switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
+#if USE_X11
+            case GST_VAAPI_DISPLAY_TYPE_X11:
+            case GST_VAAPI_DISPLAY_TYPE_GLX:
+                XSelectInput(gst_vaapi_display_x11_get_display(display),
+                    gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)),
+                    0);
+                break;
+#endif
+            default:
+                break;
+            }
+        }
+
+        /* grab thread and mark it as NULL */
+        thread = sink->event_thread;
+        sink->event_thread = NULL;
+        sink->event_thread_cancel = TRUE;
+    }
+    GST_OBJECT_UNLOCK(sink);
+
+    /* Wait for our event thread to finish */
+    if (thread) {
+        g_thread_join(thread);
+        GST_DEBUG("xevent thread stopped");
+    }
 }
 
 static void
@@ -223,11 +395,14 @@ gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface)
     iface->set_window_handle    = gst_vaapisink_video_overlay_set_window_handle;
     iface->set_render_rectangle = gst_vaapisink_video_overlay_set_render_rectangle;
     iface->expose               = gst_vaapisink_video_overlay_expose;
+    iface->handle_events        = gst_vaapisink_set_event_handling;
 }
 
 static void
 gst_vaapisink_destroy(GstVaapiSink *sink)
 {
+    gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), FALSE);
+
     gst_buffer_replace(&sink->video_buffer, NULL);
 #if USE_GLX
     gst_vaapi_texture_replace(&sink->texture, NULL);
@@ -728,6 +903,7 @@ gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
         gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
         gst_vaapi_window_show(sink->window);
         gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
+        gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), sink->handle_events);
     }
     sink->window_width  = win_width;
     sink->window_height = win_height;
@@ -961,9 +1137,8 @@ gst_vaapisink_put_surface(
 }
 
 static GstFlowReturn
-gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
+gst_vaapisink_show_frame_unlocked(GstVaapiSink *sink, GstBuffer *src_buffer)
 {
-    GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
     GstVaapiVideoMeta *meta;
     GstVaapiSurfaceProxy *proxy;
     GstVaapiSurface *surface;
@@ -1083,6 +1258,21 @@ error:
     return GST_FLOW_EOS;
 }
 
+static GstFlowReturn
+gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
+{
+    GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
+    GstFlowReturn ret;
+
+   /* At least we need at least to protect the _set_subpictures_()
+    * call to prevent a race during subpicture desctruction.
+    * FIXME: Could use a less coarse grained lock, though: */
+    gst_vaapi_display_lock(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
+    ret = gst_vaapisink_show_frame_unlocked(sink, src_buffer);
+    gst_vaapi_display_unlock(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
+    return ret;
+}
+
 #if GST_CHECK_VERSION(1,0,0)
 static gboolean
 gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
@@ -1409,6 +1599,7 @@ gst_vaapisink_init(GstVaapiSink *sink)
     sink->video_par_n    = 1;
     sink->video_par_d    = 1;
     sink->view_id        = -1;
+    sink->handle_events  = TRUE;
     sink->foreign_window = FALSE;
     sink->fullscreen     = FALSE;
     sink->synchronous    = FALSE;
index a41e740..a4d5c28 100644 (file)
@@ -88,6 +88,9 @@ struct _GstVaapiSink {
     GstVaapiRotation    rotation_req;
     guint               color_standard;
     gint32              view_id;
+    GThread            *event_thread;
+    volatile gboolean   event_thread_cancel;
+    guint               handle_events   : 1;
     guint               foreign_window  : 1;
     guint               fullscreen      : 1;
     guint               synchronous     : 1;