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