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