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