vaapisink: listen to window size changes on X11.

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>
This commit is contained in:
Holger Kaelberer 2013-11-05 14:01:11 +01:00 committed by Gwenole Beauchesne
parent 528f486513
commit 4b61cc3cd7
2 changed files with 197 additions and 3 deletions

View file

@ -146,14 +146,23 @@ gst_vaapisink_ensure_display(GstVaapiSink *sink);
/* GstVideoOverlay interface */ /* GstVideoOverlay interface */
static void
gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay);
#if USE_X11 #if USE_X11
static gboolean static gboolean
gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id); gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
#endif #endif
static void
gst_vaapisink_set_event_handling(GstVideoOverlay *overlay, gboolean handle_events);
static GstFlowReturn static GstFlowReturn
gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer); gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
static gboolean
gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height);
static void static void
gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay, gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
guintptr window) guintptr window)
@ -179,6 +188,7 @@ gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
#if USE_X11 #if USE_X11
case GST_VAAPI_DISPLAY_TYPE_X11: case GST_VAAPI_DISPLAY_TYPE_X11:
gst_vaapisink_ensure_window_xid(sink, window); gst_vaapisink_ensure_window_xid(sink, window);
gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), sink->handle_events);
break; break;
#endif #endif
default: default:
@ -208,14 +218,176 @@ gst_vaapisink_video_overlay_set_render_rectangle(
display_rect->width, display_rect->height); 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 static void
gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay) gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay)
{ {
GstVaapiSink * const sink = GST_VAAPISINK(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); 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 static void
gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface) gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface)
@ -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_window_handle = gst_vaapisink_video_overlay_set_window_handle;
iface->set_render_rectangle = gst_vaapisink_video_overlay_set_render_rectangle; iface->set_render_rectangle = gst_vaapisink_video_overlay_set_render_rectangle;
iface->expose = gst_vaapisink_video_overlay_expose; iface->expose = gst_vaapisink_video_overlay_expose;
iface->handle_events = gst_vaapisink_set_event_handling;
} }
static void static void
gst_vaapisink_destroy(GstVaapiSink *sink) gst_vaapisink_destroy(GstVaapiSink *sink)
{ {
gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), FALSE);
gst_buffer_replace(&sink->video_buffer, NULL); gst_buffer_replace(&sink->video_buffer, NULL);
#if USE_GLX #if USE_GLX
gst_vaapi_texture_replace(&sink->texture, NULL); 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_set_fullscreen(sink->window, sink->fullscreen);
gst_vaapi_window_show(sink->window); gst_vaapi_window_show(sink->window);
gst_vaapi_window_get_size(sink->window, &win_width, &win_height); 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_width = win_width;
sink->window_height = win_height; sink->window_height = win_height;
@ -961,9 +1137,8 @@ gst_vaapisink_put_surface(
} }
static GstFlowReturn 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; GstVaapiVideoMeta *meta;
GstVaapiSurfaceProxy *proxy; GstVaapiSurfaceProxy *proxy;
GstVaapiSurface *surface; GstVaapiSurface *surface;
@ -1083,6 +1258,21 @@ error:
return GST_FLOW_EOS; 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) #if GST_CHECK_VERSION(1,0,0)
static gboolean static gboolean
gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query) 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_n = 1;
sink->video_par_d = 1; sink->video_par_d = 1;
sink->view_id = -1; sink->view_id = -1;
sink->handle_events = TRUE;
sink->foreign_window = FALSE; sink->foreign_window = FALSE;
sink->fullscreen = FALSE; sink->fullscreen = FALSE;
sink->synchronous = FALSE; sink->synchronous = FALSE;

View file

@ -88,6 +88,9 @@ struct _GstVaapiSink {
GstVaapiRotation rotation_req; GstVaapiRotation rotation_req;
guint color_standard; guint color_standard;
gint32 view_id; gint32 view_id;
GThread *event_thread;
volatile gboolean event_thread_cancel;
guint handle_events : 1;
guint foreign_window : 1; guint foreign_window : 1;
guint fullscreen : 1; guint fullscreen : 1;
guint synchronous : 1; guint synchronous : 1;