vaapisink: implement GstXOverlay::set_render_rectangle().
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapisink.c
1 /*
2  *  gstvaapisink.c - VA-API video sink
3  *
4  *  gstreamer-vaapi (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapisink
25  * @short_description: A VA-API based videosink
26  *
27  * vaapisink renders video frames to a drawable (X #Window) on a local
28  * display using the Video Acceleration (VA) API. The element will
29  * create its own internal window and render into it.
30  */
31
32 #include "config.h"
33 #include <gst/gst.h>
34 #include <gst/gstutils_version.h>
35 #include <gst/video/video.h>
36 #include <gst/video/videocontext.h>
37 #include <gst/vaapi/gstvaapivideobuffer.h>
38 #include <gst/vaapi/gstvaapivideosink.h>
39 #include <gst/vaapi/gstvaapidisplay_x11.h>
40 #include <gst/vaapi/gstvaapiwindow_x11.h>
41 #if USE_VAAPISINK_GLX
42 #include <gst/vaapi/gstvaapidisplay_glx.h>
43 #include <gst/vaapi/gstvaapiwindow_glx.h>
44 #endif
45
46 /* Supported interfaces */
47 #include <gst/interfaces/xoverlay.h>
48
49 #include "gstvaapisink.h"
50 #include "gstvaapipluginutil.h"
51
52 #define HAVE_GST_XOVERLAY_SET_WINDOW_HANDLE \
53     GST_PLUGINS_BASE_CHECK_VERSION(0,10,31)
54
55 #define HAVE_GST_XOVERLAY_SET_RENDER_RECTANGLE \
56     GST_PLUGINS_BASE_CHECK_VERSION(0,10,29)
57
58 #define GST_PLUGIN_NAME "vaapisink"
59 #define GST_PLUGIN_DESC "A VA-API based videosink"
60
61 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
62 #define GST_CAT_DEFAULT gst_debug_vaapisink
63
64 /* ElementFactory information */
65 static const GstElementDetails gst_vaapisink_details =
66     GST_ELEMENT_DETAILS(
67         "VA-API sink",
68         "Sink/Video",
69         GST_PLUGIN_DESC,
70         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
71
72 /* Default template */
73 static GstStaticPadTemplate gst_vaapisink_sink_factory =
74     GST_STATIC_PAD_TEMPLATE(
75         "sink",
76         GST_PAD_SINK,
77         GST_PAD_ALWAYS,
78         GST_STATIC_CAPS(GST_VAAPI_SURFACE_CAPS));
79
80 static void gst_vaapisink_iface_init(GType type);
81
82 GST_BOILERPLATE_FULL(
83     GstVaapiSink,
84     gst_vaapisink,
85     GstVideoSink,
86     GST_TYPE_VIDEO_SINK,
87     gst_vaapisink_iface_init);
88
89 enum {
90     PROP_0,
91
92     PROP_USE_GLX,
93     PROP_FULLSCREEN,
94     PROP_SYNCHRONOUS,
95     PROP_USE_REFLECTION
96 };
97
98 /* GstImplementsInterface interface */
99
100 static gboolean
101 gst_vaapisink_implements_interface_supported(
102     GstImplementsInterface *iface,
103     GType                   type
104 )
105 {
106     return (type == GST_TYPE_VIDEO_CONTEXT ||
107             type == GST_TYPE_X_OVERLAY);
108 }
109
110 static void
111 gst_vaapisink_implements_iface_init(GstImplementsInterfaceClass *iface)
112 {
113     iface->supported = gst_vaapisink_implements_interface_supported;
114 }
115
116 /* GstVaapiVideoSink interface */
117
118 static void
119 gst_vaapisink_set_video_context(GstVideoContext *context, const gchar *type,
120     const GValue *value)
121 {
122   GstVaapiSink *sink = GST_VAAPISINK (context);
123   gst_vaapi_set_display (type, value, &sink->display);
124 }
125
126 static void
127 gst_vaapisink_video_context_iface_init(GstVideoContextInterface *iface)
128 {
129     iface->set_context = gst_vaapisink_set_video_context;
130 }
131
132 /* GstXOverlay interface */
133
134 static gboolean
135 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
136
137 static GstFlowReturn
138 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
139
140 static inline void
141 _gst_vaapisink_xoverlay_set_xid(GstXOverlay *overlay, guintptr window_id)
142 {
143     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
144
145     /* Disable GLX rendering when vaapisink is using a foreign X
146        window. It's pretty much useless */
147     sink->use_glx = FALSE;
148
149     sink->foreign_window = TRUE;
150     gst_vaapisink_ensure_window_xid(sink, window_id);
151 }
152
153 #if HAVE_GST_XOVERLAY_SET_WINDOW_HANDLE
154 static void
155 gst_vaapisink_xoverlay_set_window_handle(GstXOverlay *overlay, guintptr window_id)
156 {
157     _gst_vaapisink_xoverlay_set_xid(overlay, window_id);
158 }
159 #else
160 static void
161 gst_vaapisink_xoverlay_set_xid(GstXOverlay *overlay, XID xid)
162 {
163     _gst_vaapisink_xoverlay_set_xid(overlay, xid);
164 }
165 #endif
166
167 #if HAVE_GST_XOVERLAY_SET_RENDER_RECTANGLE
168 static void
169 gst_vaapisink_xoverlay_set_render_rectangle(
170     GstXOverlay *overlay,
171     gint         x,
172     gint         y,
173     gint         width,
174     gint         height
175 )
176 {
177     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
178     GstVaapiRectangle * const display_rect = &sink->display_rect;
179
180     display_rect->x      = x;
181     display_rect->y      = y;
182     display_rect->width  = width;
183     display_rect->height = height;
184     
185     GST_DEBUG("render rect (%d,%d):%ux%u",
186               display_rect->x, display_rect->y,
187               display_rect->width, display_rect->height);
188 }
189 #endif
190
191 static void
192 gst_vaapisink_xoverlay_expose(GstXOverlay *overlay)
193 {
194     GstBaseSink * const base_sink = GST_BASE_SINK(overlay);
195     GstBuffer *buffer;
196
197     buffer = gst_base_sink_get_last_buffer(base_sink);
198     if (buffer) {
199         gst_vaapisink_show_frame(base_sink, buffer);
200         gst_buffer_unref(buffer);
201     }
202 }
203
204 static void
205 gst_vaapisink_xoverlay_iface_init(GstXOverlayClass *iface)
206 {
207 #if HAVE_GST_XOVERLAY_SET_WINDOW_HANDLE
208     iface->set_window_handle    = gst_vaapisink_xoverlay_set_window_handle;
209 #else
210     iface->set_xwindow_id       = gst_vaapisink_xoverlay_set_xid;
211 #endif
212 #if HAVE_GST_XOVERLAY_SET_RENDER_RECTANGLE
213     iface->set_render_rectangle = gst_vaapisink_xoverlay_set_render_rectangle;
214 #endif
215     iface->expose               = gst_vaapisink_xoverlay_expose;
216 }
217
218 static void
219 gst_vaapisink_iface_init(GType type)
220 {
221     const GType g_define_type_id = type;
222
223     G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
224                           gst_vaapisink_implements_iface_init);
225     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
226                           gst_vaapisink_video_context_iface_init);
227     G_IMPLEMENT_INTERFACE(GST_TYPE_X_OVERLAY,
228                           gst_vaapisink_xoverlay_iface_init);
229 }
230
231 static void
232 gst_vaapisink_destroy(GstVaapiSink *sink)
233 {
234     if (sink->display) {
235         g_object_unref(sink->display);
236         sink->display = NULL;
237     }
238 }
239
240 /* Checks whether a ConfigureNotify event is in the queue */
241 typedef struct _ConfigureNotifyEventPendingArgs ConfigureNotifyEventPendingArgs;
242 struct _ConfigureNotifyEventPendingArgs {
243     Window      window;
244     guint       width;
245     guint       height;
246     gboolean    match;
247 };
248
249 static Bool
250 configure_notify_event_pending_cb(Display *dpy, XEvent *xev, XPointer arg)
251 {
252     ConfigureNotifyEventPendingArgs * const args =
253         (ConfigureNotifyEventPendingArgs *)arg;
254
255     if (xev->type == ConfigureNotify &&
256         xev->xconfigure.window == args->window &&
257         xev->xconfigure.width  == args->width  &&
258         xev->xconfigure.height == args->height)
259         args->match = TRUE;
260
261     /* XXX: this is a hack to traverse the whole queue because we
262        can't use XPeekIfEvent() since it could block */
263     return False;
264 }
265
266 static gboolean
267 configure_notify_event_pending(
268     GstVaapiSink *sink,
269     Window        window,
270     guint         width,
271     guint         height
272 )
273 {
274     ConfigureNotifyEventPendingArgs args;
275     XEvent xev;
276
277     args.window = window;
278     args.width  = width;
279     args.height = height;
280     args.match  = FALSE;
281
282     /* XXX: don't use XPeekIfEvent() because it might block */
283     XCheckIfEvent(
284         gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(sink->display)),
285         &xev,
286         configure_notify_event_pending_cb, (XPointer)&args
287     );
288     return args.match;
289 }
290
291 static gboolean
292 gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
293 {
294     GstVaapiRectangle * const display_rect = &sink->display_rect;
295     guint num, den, display_par_n, display_par_d;
296     double display_ratio;
297     gboolean success;
298
299     GST_DEBUG("ensure render rect within %ux%u bounds", width, height);
300
301     gst_vaapi_display_get_pixel_aspect_ratio(
302         sink->display,
303         &display_par_n, &display_par_d
304     );
305     GST_DEBUG("display pixel-aspect-ratio %d/%d",
306               display_par_n, display_par_d);
307
308     success = gst_video_calculate_display_ratio(
309         &num, &den,
310         sink->video_width, sink->video_height,
311         sink->video_par_n, sink->video_par_d,
312         display_par_n, display_par_d
313     );
314     if (!success)
315         return FALSE;
316     GST_DEBUG("video size %dx%d, calculated ratio %d/%d",
317               sink->video_width, sink->video_height, num, den);
318
319     if ((sink->video_height % den) == 0) {
320         GST_DEBUG("keeping video height");
321         display_rect->width  =
322             gst_util_uint64_scale_int(sink->video_height, num, den);
323         display_rect->height = sink->video_height;
324     }
325     else if ((sink->video_width % num) == 0) {
326         GST_DEBUG("keeping video width");
327         display_rect->width  = sink->video_width;
328         display_rect->height =
329             gst_util_uint64_scale_int(sink->video_width, den, num);
330     }
331     else {
332         GST_DEBUG("approximating while keeping video height");
333         display_rect->width  =
334             gst_util_uint64_scale_int(sink->video_height, num, den);
335         display_rect->height = sink->video_height;
336     }
337     display_ratio = (gdouble)display_rect->width / display_rect->height;
338     GST_DEBUG("scaling to %ux%u", display_rect->width, display_rect->height);
339
340     if (sink->fullscreen || sink->foreign_window ||
341         display_rect->width > width || display_rect->height > height) {
342         if (sink->video_width > sink->video_height) {
343             display_rect->width  = width;
344             display_rect->height = width / display_ratio;
345         }
346         else {
347             display_rect->width  = height * display_ratio;
348             display_rect->height = height;
349         }
350     }
351
352     if (sink->fullscreen) {
353         display_rect->x  = (width  - display_rect->width)  / 2;
354         display_rect->y  = (height - display_rect->height) / 2;
355     }
356     else {
357         display_rect->x  = 0;
358         display_rect->y  = 0;
359     }
360
361     GST_DEBUG("render rect (%d,%d):%ux%u",
362               display_rect->x, display_rect->y,
363               display_rect->width, display_rect->height);
364     return TRUE;
365 }
366
367 static inline gboolean
368 gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
369 {
370     GstVaapiDisplay * const display = sink->display;
371
372     if (!sink->window) {
373 #if USE_VAAPISINK_GLX
374         if (sink->use_glx)
375             sink->window = gst_vaapi_window_glx_new(display, width, height);
376         else
377 #endif
378             sink->window = gst_vaapi_window_x11_new(display, width, height);
379         if (sink->window)
380             gst_x_overlay_got_xwindow_id(
381                 GST_X_OVERLAY(sink),
382                 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window))
383             );
384     }
385     return sink->window != NULL;
386 }
387
388 static gboolean
389 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id)
390 {
391     Window rootwin;
392     unsigned int width, height, border_width, depth;
393     int x, y;
394     XID xid = window_id;
395
396     if (!gst_vaapi_ensure_display(sink, &sink->display))
397         return FALSE;
398
399     gst_vaapi_display_lock(sink->display);
400     XGetGeometry(
401         gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(sink->display)),
402         xid,
403         &rootwin,
404         &x, &y, &width, &height, &border_width, &depth
405     );
406     gst_vaapi_display_unlock(sink->display);
407
408     if ((width != sink->window_width || height != sink->window_height) &&
409         !configure_notify_event_pending(sink, xid, width, height)) {
410         if (!gst_vaapisink_ensure_render_rect(sink, width, height))
411             return FALSE;
412         sink->window_width  = width;
413         sink->window_height = height;
414     }
415
416     if (sink->window &&
417         gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)) == xid)
418         return TRUE;
419
420     if (sink->window) {
421         g_object_unref(sink->window);
422         sink->window = NULL;
423     }
424
425 #if USE_VAAPISINK_GLX
426     if (sink->use_glx)
427         sink->window = gst_vaapi_window_glx_new_with_xid(sink->display, xid);
428     else
429 #endif
430         sink->window = gst_vaapi_window_x11_new_with_xid(sink->display, xid);
431     return sink->window != NULL;
432 }
433
434 static gboolean
435 gst_vaapisink_start(GstBaseSink *base_sink)
436 {
437     return TRUE;
438 }
439
440 static gboolean
441 gst_vaapisink_stop(GstBaseSink *base_sink)
442 {
443     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
444
445     if (sink->window) {
446         g_object_unref(sink->window);
447         sink->window = NULL;
448     }
449
450     if (sink->display) {
451         g_object_unref(sink->display);
452         sink->display = NULL;
453     }
454     return TRUE;
455 }
456
457 static gboolean
458 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
459 {
460     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
461     GstStructure * const structure = gst_caps_get_structure(caps, 0);
462     guint display_width, display_height, win_width, win_height;
463     gint video_width, video_height, video_par_n = 1, video_par_d = 1;
464
465     if (!structure)
466         return FALSE;
467     if (!gst_structure_get_int(structure, "width",  &video_width))
468         return FALSE;
469     if (!gst_structure_get_int(structure, "height", &video_height))
470         return FALSE;
471     sink->video_width  = video_width;
472     sink->video_height = video_height;
473
474     gst_video_parse_caps_pixel_aspect_ratio(caps, &video_par_n, &video_par_d);
475     sink->video_par_n  = video_par_n;
476     sink->video_par_d  = video_par_d;
477     GST_DEBUG("video pixel-aspect-ratio %d/%d", video_par_n, video_par_d);
478
479     if (!gst_vaapi_ensure_display(sink, &sink->display))
480         return FALSE;
481
482     gst_vaapi_display_get_size(sink->display, &display_width, &display_height);
483     if (!gst_vaapisink_ensure_render_rect(sink, display_width, display_height))
484         return FALSE;
485
486     if (sink->fullscreen) {
487         win_width  = display_width;
488         win_height = display_height;
489     }
490     else {
491         win_width  = sink->display_rect.width;
492         win_height = sink->display_rect.height;
493     }
494
495     if (sink->window)
496         gst_vaapi_window_set_size(sink->window, win_width, win_height);
497     else {
498         gst_vaapi_display_lock(sink->display);
499         gst_x_overlay_prepare_xwindow_id(GST_X_OVERLAY(sink));
500         gst_vaapi_display_unlock(sink->display);
501         if (sink->window)
502             return TRUE;
503         if (!gst_vaapisink_ensure_window(sink, win_width, win_height))
504             return FALSE;
505         gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
506         gst_vaapi_window_show(sink->window);
507     }
508     sink->window_width  = win_width;
509     sink->window_height = win_height;
510     return TRUE;
511 }
512
513 #if USE_VAAPISINK_GLX
514 static void
515 render_background(GstVaapiSink *sink)
516 {
517     /* Original code from Mirco Muller (MacSlow):
518        <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
519     GLfloat fStartX = 0.0f;
520     GLfloat fStartY = 0.0f;
521     GLfloat fWidth  = (GLfloat)sink->window_width;
522     GLfloat fHeight = (GLfloat)sink->window_height;
523
524     glClear(GL_COLOR_BUFFER_BIT);
525     glBegin(GL_QUADS);
526     {
527         /* top third, darker grey to white */
528         glColor3f(0.85f, 0.85f, 0.85f);
529         glVertex3f(fStartX, fStartY, 0.0f);
530         glColor3f(0.85f, 0.85f, 0.85f);
531         glVertex3f(fStartX + fWidth, fStartY, 0.0f);
532         glColor3f(1.0f, 1.0f, 1.0f);
533         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
534         glColor3f(1.0f, 1.0f, 1.0f);
535         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
536
537         /* middle third, just plain white */
538         glColor3f(1.0f, 1.0f, 1.0f);
539         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
540         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
541         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
542         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
543
544         /* bottom third, white to lighter grey */
545         glColor3f(1.0f, 1.0f, 1.0f);
546         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
547         glColor3f(1.0f, 1.0f, 1.0f);
548         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
549         glColor3f(0.62f, 0.66f, 0.69f);
550         glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
551         glColor3f(0.62f, 0.66f, 0.69f);
552         glVertex3f(fStartX, fStartY + fHeight, 0.0f);
553     }
554     glEnd();
555 }
556
557 static void
558 render_frame(GstVaapiSink *sink)
559 {
560     const guint x1 = sink->display_rect.x;
561     const guint x2 = sink->display_rect.x + sink->display_rect.width;
562     const guint y1 = sink->display_rect.y;
563     const guint y2 = sink->display_rect.y + sink->display_rect.height;
564
565     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
566     glBegin(GL_QUADS);
567     {
568         glTexCoord2f(0.0f, 0.0f); glVertex2i(x1, y1);
569         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y2);
570         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y2);
571         glTexCoord2f(1.0f, 0.0f); glVertex2i(x2, y1);
572     }
573     glEnd();
574 }
575
576 static void
577 render_reflection(GstVaapiSink *sink)
578 {
579     const guint x1 = sink->display_rect.x;
580     const guint x2 = sink->display_rect.x + sink->display_rect.width;
581     const guint y1 = sink->display_rect.y;
582     const guint rh = sink->display_rect.height / 5;
583     GLfloat     ry = 1.0f - (GLfloat)rh / (GLfloat)sink->display_rect.height;
584
585     glBegin(GL_QUADS);
586     {
587         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
588         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y1);
589         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y1);
590
591         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
592         glTexCoord2f(1.0f, ry); glVertex2i(x2, y1 + rh);
593         glTexCoord2f(0.0f, ry); glVertex2i(x1, y1 + rh);
594     }
595     glEnd();
596 }
597
598 static gboolean
599 gst_vaapisink_show_frame_glx(
600     GstVaapiSink    *sink,
601     GstVaapiSurface *surface,
602     guint            flags
603 )
604 {
605     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(sink->window);
606     GLenum target;
607     GLuint texture;
608
609     gst_vaapi_window_glx_make_current(window);
610     if (!sink->texture) {
611         sink->texture = gst_vaapi_texture_new(
612             sink->display,
613             GL_TEXTURE_2D,
614             GL_BGRA,
615             sink->video_width,
616             sink->video_height
617         );
618         if (!sink->texture)
619             goto error_create_texture;
620     }
621     if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
622         goto error_transfer_surface;
623
624     target  = gst_vaapi_texture_get_target(sink->texture);
625     texture = gst_vaapi_texture_get_id(sink->texture);
626     if (target != GL_TEXTURE_2D || !texture)
627         return FALSE;
628
629     if (sink->use_reflection)
630         render_background(sink);
631
632     glEnable(target);
633     glBindTexture(target, texture);
634     {
635         if (sink->use_reflection) {
636             glPushMatrix();
637             glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
638             glTranslatef(50.0f, 0.0f, 0.0f);
639         }
640         render_frame(sink);
641         if (sink->use_reflection) {
642             glPushMatrix();
643             glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
644             render_reflection(sink);
645             glPopMatrix();
646             glPopMatrix();
647         }
648     }
649     glBindTexture(target, 0);
650     glDisable(target);
651     gst_vaapi_window_glx_swap_buffers(window);
652     return TRUE;
653
654     /* ERRORS */
655 error_create_texture:
656     {
657         GST_DEBUG("could not create VA/GLX texture");
658         return FALSE;
659     }
660 error_transfer_surface:
661     {
662         GST_DEBUG("could not transfer VA surface to texture");
663         return FALSE;
664     }
665 }
666 #endif
667
668 static inline gboolean
669 gst_vaapisink_show_frame_x11(
670     GstVaapiSink    *sink,
671     GstVaapiSurface *surface,
672     guint            flags
673 )
674 {
675     if (!gst_vaapi_window_put_surface(sink->window, surface,
676                 NULL, &sink->display_rect, flags)) {
677         GST_DEBUG("could not render VA surface");
678         return FALSE;
679     }
680     return TRUE;
681 }
682
683 static GstFlowReturn
684 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer)
685 {
686     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
687     GstVaapiVideoBuffer * const vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
688     GstVaapiSurface *surface;
689     guint flags;
690     gboolean success;
691     GstVideoOverlayComposition * const composition =
692         gst_video_buffer_get_overlay_composition(buffer);
693
694     if (sink->display != gst_vaapi_video_buffer_get_display (vbuffer)) {
695       if (sink->display)
696         g_object_unref (sink->display);
697       sink->display = g_object_ref (gst_vaapi_video_buffer_get_display (vbuffer));
698     }
699
700     if (!sink->window)
701         return GST_FLOW_UNEXPECTED;
702
703     surface = gst_vaapi_video_buffer_get_surface(vbuffer);
704     if (!surface)
705         return GST_FLOW_UNEXPECTED;
706
707     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
708               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
709
710     flags = GST_VAAPI_PICTURE_STRUCTURE_FRAME;
711
712     if (!gst_vaapi_surface_set_subpictures_from_composition(surface,
713              composition, TRUE))
714         GST_WARNING("could not update subtitles");
715
716 #if USE_VAAPISINK_GLX
717     if (sink->use_glx)
718         success = gst_vaapisink_show_frame_glx(sink, surface, flags);
719     else
720 #endif
721         success = gst_vaapisink_show_frame_x11(sink, surface, flags);
722     return success ? GST_FLOW_OK : GST_FLOW_UNEXPECTED;
723 }
724
725 static gboolean
726 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
727 {
728     GstVaapiSink *sink = GST_VAAPISINK(base_sink);
729     GST_DEBUG ("sharing display %p", sink->display);
730     return gst_vaapi_reply_to_query (query, sink->display);
731 }
732
733 static void
734 gst_vaapisink_finalize(GObject *object)
735 {
736     gst_vaapisink_destroy(GST_VAAPISINK(object));
737
738     G_OBJECT_CLASS(parent_class)->finalize(object);
739 }
740
741 static void
742 gst_vaapisink_set_property(
743     GObject      *object,
744     guint         prop_id,
745     const GValue *value,
746     GParamSpec   *pspec
747 )
748 {
749     GstVaapiSink * const sink = GST_VAAPISINK(object);
750
751     switch (prop_id) {
752     case PROP_USE_GLX:
753         sink->use_glx = g_value_get_boolean(value);
754         break;
755     case PROP_FULLSCREEN:
756         sink->fullscreen = g_value_get_boolean(value);
757         break;
758     case PROP_SYNCHRONOUS:
759         sink->synchronous = g_value_get_boolean(value);
760         break;
761     case PROP_USE_REFLECTION:
762         sink->use_reflection = g_value_get_boolean(value);
763         break;
764     default:
765         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
766         break;
767     }
768 }
769
770 static void
771 gst_vaapisink_get_property(
772     GObject    *object,
773     guint       prop_id,
774     GValue     *value,
775     GParamSpec *pspec
776 )
777 {
778     GstVaapiSink * const sink = GST_VAAPISINK(object);
779
780     switch (prop_id) {
781     case PROP_USE_GLX:
782         g_value_set_boolean(value, sink->use_glx);
783         break;
784     case PROP_FULLSCREEN:
785         g_value_set_boolean(value, sink->fullscreen);
786         break;
787     case PROP_SYNCHRONOUS:
788         g_value_set_boolean(value, sink->synchronous);
789         break;
790     case PROP_USE_REFLECTION:
791         g_value_set_boolean(value, sink->use_reflection);
792         break;
793     default:
794         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
795         break;
796     }
797 }
798
799 static void
800 gst_vaapisink_base_init(gpointer klass)
801 {
802     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
803
804     gst_element_class_set_details(element_class, &gst_vaapisink_details);
805
806     gst_element_class_add_pad_template(
807         element_class,
808         gst_static_pad_template_get(&gst_vaapisink_sink_factory)
809     );
810 }
811
812 static void
813 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
814 {
815     GObjectClass * const     object_class   = G_OBJECT_CLASS(klass);
816     GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
817
818     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
819                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
820
821     object_class->finalize       = gst_vaapisink_finalize;
822     object_class->set_property   = gst_vaapisink_set_property;
823     object_class->get_property   = gst_vaapisink_get_property;
824
825     basesink_class->start        = gst_vaapisink_start;
826     basesink_class->stop         = gst_vaapisink_stop;
827     basesink_class->set_caps     = gst_vaapisink_set_caps;
828     basesink_class->preroll      = gst_vaapisink_show_frame;
829     basesink_class->render       = gst_vaapisink_show_frame;
830     basesink_class->query        = gst_vaapisink_query;
831
832 #if USE_VAAPISINK_GLX
833     g_object_class_install_property
834         (object_class,
835          PROP_USE_GLX,
836          g_param_spec_boolean("use-glx",
837                               "GLX rendering",
838                               "Enables GLX rendering",
839                               TRUE,
840                               G_PARAM_READWRITE));
841
842     g_object_class_install_property
843         (object_class,
844          PROP_USE_REFLECTION,
845          g_param_spec_boolean("use-reflection",
846                               "Reflection effect",
847                               "Enables OpenGL reflection effect",
848                               FALSE,
849                               G_PARAM_READWRITE));
850 #endif
851
852     g_object_class_install_property
853         (object_class,
854          PROP_FULLSCREEN,
855          g_param_spec_boolean("fullscreen",
856                               "Fullscreen",
857                               "Requests window in fullscreen state",
858                               FALSE,
859                               G_PARAM_READWRITE));
860
861     /**
862      * GstVaapiSink:synchronous:
863      *
864      * When enabled, runs the X display in synchronous mode. Note that
865      * this is used only for debugging.
866      */
867     g_object_class_install_property
868         (object_class,
869          PROP_SYNCHRONOUS,
870          g_param_spec_boolean("synchronous",
871                               "Synchronous mode",
872                               "Toggles X display synchronous mode",
873                               FALSE,
874                               G_PARAM_READWRITE));
875 }
876
877 static void
878 gst_vaapisink_init(GstVaapiSink *sink, GstVaapiSinkClass *klass)
879 {
880     sink->display        = NULL;
881     sink->window         = NULL;
882     sink->window_width   = 0;
883     sink->window_height  = 0;
884     sink->texture        = NULL;
885     sink->video_width    = 0;
886     sink->video_height   = 0;
887     sink->video_par_n    = 1;
888     sink->video_par_d    = 1;
889     sink->foreign_window = FALSE;
890     sink->fullscreen     = FALSE;
891     sink->synchronous    = FALSE;
892     sink->use_glx        = USE_VAAPISINK_GLX;
893     sink->use_reflection = FALSE;
894 }