Tizen 2.1 base
[framework/multimedia/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiwindow_glx.c
1 /*
2  *  gstvaapiwindow_glx.c - VA/GLX window abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library 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 GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * SECTION:gstvaapiwindow_glx
24  * @short_description: VA/GLX window abstraction
25  */
26
27 #include "sysdeps.h"
28 #include "gstvaapiwindow_glx.h"
29 #include "gstvaapidisplay_x11.h"
30 #include "gstvaapidisplay_x11_priv.h"
31 #include "gstvaapiutils_x11.h"
32 #include "gstvaapiutils_glx.h"
33 #include "gstvaapi_priv.h"
34
35 #define DEBUG 1
36 #include "gstvaapidebug.h"
37
38 G_DEFINE_TYPE(GstVaapiWindowGLX,
39               gst_vaapi_window_glx,
40               GST_VAAPI_TYPE_WINDOW_X11);
41
42 #define GST_VAAPI_WINDOW_GLX_GET_PRIVATE(obj)                   \
43     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
44                                  GST_VAAPI_TYPE_WINDOW_GLX,     \
45                                  GstVaapiWindowGLXPrivate))
46
47 struct _GstVaapiWindowGLXPrivate {
48     Colormap            cmap;
49     GLContextState     *gl_context;
50     guint               is_constructed  : 1;
51     guint               foreign_window  : 1;
52 };
53
54 enum {
55     PROP_0,
56
57     PROP_GLX_CONTEXT
58 };
59
60 /* Fill rectangle coords with capped bounds */
61 static inline void
62 fill_rect(
63     GstVaapiRectangle       *dst_rect,
64     const GstVaapiRectangle *src_rect,
65     guint                    width,
66     guint                    height
67 )
68 {
69     if (src_rect) {
70         dst_rect->x = src_rect->x > 0 ? src_rect->x : 0;
71         dst_rect->y = src_rect->y > 0 ? src_rect->y : 0;
72         if (src_rect->x + src_rect->width < width)
73             dst_rect->width = src_rect->width;
74         else
75             dst_rect->width = width - dst_rect->x;
76         if (src_rect->y + src_rect->height < height)
77             dst_rect->height = src_rect->height;
78         else
79             dst_rect->height = height - dst_rect->y;
80     }
81     else {
82         dst_rect->x      = 0;
83         dst_rect->y      = 0;
84         dst_rect->width  = width;
85         dst_rect->height = height;
86     }
87 }
88
89 static void
90 _gst_vaapi_window_glx_destroy_context(GstVaapiWindowGLX *window)
91 {
92     GstVaapiWindowGLXPrivate * const priv = window->priv;
93
94     GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
95     if (priv->gl_context) {
96         gl_destroy_context(priv->gl_context);
97         priv->gl_context = NULL;
98     }
99     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
100 }
101
102 static gboolean
103 _gst_vaapi_window_glx_create_context(
104     GstVaapiWindowGLX *window,
105     GLXContext         foreign_context
106 )
107 {
108     GstVaapiWindowGLXPrivate * const priv = window->priv;
109     Display * const                  dpy  = GST_VAAPI_OBJECT_XDISPLAY(window);
110     GLContextState                   parent_cs;
111
112     parent_cs.display = dpy;
113     parent_cs.window  = None;
114     parent_cs.context = foreign_context;
115
116     GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
117     priv->gl_context = gl_create_context(
118         dpy,
119         GST_VAAPI_OBJECT_XSCREEN(window),
120         &parent_cs
121     );
122     if (!priv->gl_context) {
123         GST_DEBUG("could not create GLX context");
124         goto end;
125     }
126
127     if (!glXIsDirect(dpy, priv->gl_context->context)) {
128         GST_DEBUG("could not create a direct-rendering GLX context");
129         goto out_destroy_context;
130     }
131     goto end;
132
133 out_destroy_context:
134     gl_destroy_context(priv->gl_context);
135     priv->gl_context = NULL;
136 end:
137     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
138     return priv->gl_context != NULL;
139 }
140
141 static gboolean
142 _gst_vaapi_window_glx_ensure_context(
143     GstVaapiWindowGLX *window,
144     GLXContext         foreign_context
145 )
146 {
147     GstVaapiWindowGLXPrivate * const priv = window->priv;
148
149     if (priv->gl_context) {
150         if (!foreign_context || foreign_context == priv->gl_context->context)
151             return TRUE;
152         _gst_vaapi_window_glx_destroy_context(window);
153     }
154     return _gst_vaapi_window_glx_create_context(window, foreign_context);
155 }
156
157 static gboolean
158 gst_vaapi_window_glx_ensure_context(
159     GstVaapiWindowGLX *window,
160     GLXContext         foreign_context
161 )
162 {
163     GstVaapiWindowGLXPrivate * const priv = window->priv;
164     GLContextState old_cs;
165     guint width, height;
166
167     if (!_gst_vaapi_window_glx_ensure_context(window, foreign_context))
168         return FALSE;
169
170     priv->gl_context->window = GST_VAAPI_OBJECT_ID(window);
171     if (!gl_set_current_context(priv->gl_context, &old_cs)) {
172         GST_DEBUG("could not make newly created GLX context current");
173         return FALSE;
174     }
175
176     glDisable(GL_DEPTH_TEST);
177     glDepthMask(GL_FALSE);
178     glDisable(GL_CULL_FACE);
179     glDrawBuffer(GL_BACK);
180     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
181     glEnable(GL_BLEND);
182     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
183
184     gst_vaapi_window_get_size(GST_VAAPI_WINDOW(window), &width, &height);
185     gl_resize(width, height);
186
187     gl_set_bgcolor(0);
188     glClear(GL_COLOR_BUFFER_BIT);
189     gl_set_current_context(&old_cs, NULL);
190     return TRUE;
191 }
192
193 static Visual *
194 gst_vaapi_window_glx_get_visual(GstVaapiWindow *window)
195 {
196     GstVaapiWindowGLX * const glx_window = GST_VAAPI_WINDOW_GLX(window);
197
198     if (!_gst_vaapi_window_glx_ensure_context(glx_window, NULL))
199         return NULL;
200     return glx_window->priv->gl_context->visual->visual;
201 }
202
203 static void
204 gst_vaapi_window_glx_destroy_colormap(GstVaapiWindowGLX *window)
205 {
206     GstVaapiWindowGLXPrivate * const priv = window->priv;
207     Display * const                  dpy  = GST_VAAPI_OBJECT_XDISPLAY(window);
208
209     if (priv->cmap) {
210         if (!priv->foreign_window) {
211             GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
212             XFreeColormap(dpy, priv->cmap);
213             GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
214         }
215         priv->cmap = None;
216     }
217 }
218
219 static Colormap
220 gst_vaapi_window_glx_create_colormap(GstVaapiWindowGLX *window)
221 {
222     GstVaapiWindowGLXPrivate * const priv = window->priv;
223     Display * const                  dpy  = GST_VAAPI_OBJECT_XDISPLAY(window);
224     int                              screen;
225     XWindowAttributes                wattr;
226     gboolean                         success = FALSE;
227
228     if (!priv->cmap) {
229         if (!priv->foreign_window) {
230             if (!_gst_vaapi_window_glx_ensure_context(window, NULL))
231                 return None;
232             GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
233             x11_trap_errors();
234             /* XXX: add a GstVaapiDisplayX11:x11-screen property? */
235             screen     = GST_VAAPI_OBJECT_XSCREEN(window);
236             priv->cmap = XCreateColormap(
237                 dpy,
238                 RootWindow(dpy, screen),
239                 priv->gl_context->visual->visual,
240                 AllocNone
241             );
242             success = x11_untrap_errors() == 0;
243             GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
244         }
245         else {
246             GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
247             x11_trap_errors();
248             XGetWindowAttributes(dpy, GST_VAAPI_OBJECT_ID(window), &wattr);
249             priv->cmap = wattr.colormap;
250             success = x11_untrap_errors() == 0;
251             GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
252         }
253         if (!success)
254             return None;
255     }
256     return priv->cmap;
257 }
258
259 static Colormap
260 gst_vaapi_window_glx_get_colormap(GstVaapiWindow *window)
261 {
262     return gst_vaapi_window_glx_create_colormap(GST_VAAPI_WINDOW_GLX(window));
263 }
264
265 static gboolean
266 gst_vaapi_window_glx_resize(GstVaapiWindow *window, guint width, guint height)
267 {
268     GstVaapiWindowGLXPrivate * const priv = GST_VAAPI_WINDOW_GLX(window)->priv;
269     Display * const                  dpy  = GST_VAAPI_OBJECT_XDISPLAY(window);
270     GLContextState                   old_cs;
271
272     if (!GST_VAAPI_WINDOW_CLASS(gst_vaapi_window_glx_parent_class)->
273         resize(window, width, height))
274         return FALSE;
275
276     GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
277     XSync(dpy, False); /* make sure resize completed */
278     if (gl_set_current_context(priv->gl_context, &old_cs)) {
279         gl_resize(width, height);
280         gl_set_current_context(&old_cs, NULL);
281     }
282     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
283     return TRUE;
284 }
285
286 static void
287 gst_vaapi_window_glx_finalize(GObject *object)
288 {
289     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(object);
290
291     _gst_vaapi_window_glx_destroy_context(window);
292     gst_vaapi_window_glx_destroy_colormap(window);
293
294     G_OBJECT_CLASS(gst_vaapi_window_glx_parent_class)->finalize(object);
295 }
296
297 static void
298 gst_vaapi_window_glx_set_property(
299     GObject      *object,
300     guint         prop_id,
301     const GValue *value,
302     GParamSpec   *pspec
303 )
304 {
305     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(object);
306
307     switch (prop_id) {
308     case PROP_GLX_CONTEXT:
309         gst_vaapi_window_glx_set_context(window, g_value_get_pointer(value));
310         break;
311     default:
312         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
313         break;
314     }
315 }
316
317 static void
318 gst_vaapi_window_glx_get_property(
319     GObject    *object,
320     guint       prop_id,
321     GValue     *value,
322     GParamSpec *pspec
323 )
324 {
325     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(object);
326
327     switch (prop_id) {
328     case PROP_GLX_CONTEXT:
329         g_value_set_pointer(value, gst_vaapi_window_glx_get_context(window));
330         break;
331     default:
332         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
333         break;
334     }
335 }
336
337 static void
338 gst_vaapi_window_glx_constructed(GObject *object)
339 {
340     GstVaapiWindowGLXPrivate * const priv = GST_VAAPI_WINDOW_GLX(object)->priv;
341     GObjectClass *parent_class;
342
343     parent_class = G_OBJECT_CLASS(gst_vaapi_window_glx_parent_class);
344     if (parent_class->constructed)
345         parent_class->constructed(object);
346
347     priv->foreign_window =
348         gst_vaapi_window_x11_is_foreign_xid(GST_VAAPI_WINDOW_X11(object));
349
350     priv->is_constructed =
351         gst_vaapi_window_glx_ensure_context(GST_VAAPI_WINDOW_GLX(object), NULL);
352 }
353
354 static void
355 gst_vaapi_window_glx_class_init(GstVaapiWindowGLXClass *klass)
356 {
357     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
358     GstVaapiWindowClass * const win_class = GST_VAAPI_WINDOW_CLASS(klass);
359     GstVaapiWindowX11Class * const xwin_class = GST_VAAPI_WINDOW_X11_CLASS(klass);
360
361     g_type_class_add_private(klass, sizeof(GstVaapiWindowGLXPrivate));
362
363     object_class->finalize      = gst_vaapi_window_glx_finalize;
364     object_class->set_property  = gst_vaapi_window_glx_set_property;
365     object_class->get_property  = gst_vaapi_window_glx_get_property;
366     object_class->constructed   = gst_vaapi_window_glx_constructed;
367
368     win_class->resize           = gst_vaapi_window_glx_resize;
369     xwin_class->get_visual      = gst_vaapi_window_glx_get_visual;
370     xwin_class->get_colormap    = gst_vaapi_window_glx_get_colormap;
371
372     /**
373      * GstVaapiDisplayGLX:glx-context:
374      *
375      * The GLX context that was created by gst_vaapi_window_glx_new()
376      * or that was bound from gst_vaapi_window_glx_set_context().
377      */
378     g_object_class_install_property
379         (object_class,
380          PROP_GLX_CONTEXT,
381          g_param_spec_pointer("glx-context",
382                               "GLX context",
383                               "GLX context",
384                               G_PARAM_READWRITE));
385 }
386
387 static void
388 gst_vaapi_window_glx_init(GstVaapiWindowGLX *window)
389 {
390     GstVaapiWindowGLXPrivate *priv = GST_VAAPI_WINDOW_GLX_GET_PRIVATE(window);
391
392     window->priv                = priv;
393     priv->cmap                  = None;
394     priv->gl_context            = NULL;
395     priv->is_constructed        = FALSE;
396     priv->foreign_window        = FALSE;
397 }
398
399 /**
400  * gst_vaapi_window_glx_new:
401  * @display: a #GstVaapiDisplay
402  * @width: the requested window width, in pixels
403  * @height: the requested windo height, in pixels
404  *
405  * Creates a window with the specified @width and @height. The window
406  * will be attached to the @display and remains invisible to the user
407  * until gst_vaapi_window_show() is called.
408  *
409  * Return value: the newly allocated #GstVaapiWindow object
410  */
411 GstVaapiWindow *
412 gst_vaapi_window_glx_new(GstVaapiDisplay *display, guint width, guint height)
413 {
414     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
415     g_return_val_if_fail(width  > 0, NULL);
416     g_return_val_if_fail(height > 0, NULL);
417
418     return g_object_new(GST_VAAPI_TYPE_WINDOW_GLX,
419                         "display", display,
420                         "id",      GST_VAAPI_ID(None),
421                         "width",   width,
422                         "height",  height,
423                         NULL);
424 }
425
426 /**
427  * gst_vaapi_window_glx_new_with_xid:
428  * @display: a #GstVaapiDisplay
429  * @xid: an X11 #Window id
430  *
431  * Creates a #GstVaapiWindow using the X11 #Window @xid. The caller
432  * still owns the window and must call XDestroyWindow() when all
433  * #GstVaapiWindow references are released. Doing so too early can
434  * yield undefined behaviour.
435  *
436  * Return value: the newly allocated #GstVaapiWindow object
437  */
438 GstVaapiWindow *
439 gst_vaapi_window_glx_new_with_xid(GstVaapiDisplay *display, Window xid)
440 {
441     GST_DEBUG("new window from xid 0x%08x", xid);
442
443     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
444     g_return_val_if_fail(xid != None, NULL);
445
446     return g_object_new(GST_VAAPI_TYPE_WINDOW_GLX,
447                         "display", display,
448                         "id",      GST_VAAPI_ID(xid),
449                         NULL);
450 }
451
452 /**
453  * gst_vaapi_window_glx_get_context:
454  * @window: a #GstVaapiWindowGLX
455  *
456  * Returns the #GLXContext bound to the @window.
457  *
458  * Return value: the #GLXContext bound to the @window
459  */
460 GLXContext
461 gst_vaapi_window_glx_get_context(GstVaapiWindowGLX *window)
462 {
463     g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), NULL);
464     g_return_val_if_fail(window->priv->is_constructed, FALSE);
465
466     return window->priv->gl_context->context;
467 }
468
469 /**
470  * gst_vaapi_window_glx_set_context:
471  * @window: a #GstVaapiWindowGLX
472  * @ctx: a GLX context
473  *
474  * Binds GLX context @ctx to @window. If @ctx is non %NULL, the caller
475  * is responsible to making sure it has compatible visual with that of
476  * the underlying X window. If @ctx is %NULL, a new context is created
477  * and the @window owns it.
478  *
479  * Return value: %TRUE on success
480  */
481 gboolean
482 gst_vaapi_window_glx_set_context(GstVaapiWindowGLX *window, GLXContext ctx)
483 {
484     g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), FALSE);
485     g_return_val_if_fail(window->priv->is_constructed, FALSE);
486
487     return gst_vaapi_window_glx_ensure_context(window, ctx);
488 }
489
490 /**
491  * gst_vaapi_window_glx_make_current:
492  * @window: a #GstVaapiWindowGLX
493  *
494  * Makes the @window GLX context the current GLX rendering context of
495  * the calling thread, replacing the previously current context if
496  * there was one.
497  *
498  * Return value: %TRUE on success
499  */
500 gboolean
501 gst_vaapi_window_glx_make_current(GstVaapiWindowGLX *window)
502 {
503     gboolean success;
504
505     g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), FALSE);
506     g_return_val_if_fail(window->priv->is_constructed, FALSE);
507
508     GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
509     success = gl_set_current_context(window->priv->gl_context, NULL);
510     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
511     return success;
512 }
513
514 /**
515  * gst_vaapi_window_glx_swap_buffers:
516  * @window: a #GstVaapiWindowGLX
517  *
518  * Promotes the contents of the back buffer of @window to become the
519  * contents of the front buffer of @window. This simply is wrapper
520  * around glXSwapBuffers().
521  */
522 void
523 gst_vaapi_window_glx_swap_buffers(GstVaapiWindowGLX *window)
524 {
525     g_return_if_fail(GST_VAAPI_IS_WINDOW_GLX(window));
526     g_return_if_fail(window->priv->is_constructed);
527
528     GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
529     gl_swap_buffers(window->priv->gl_context);
530     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
531 }
532
533 /**
534  * gst_vaapi_window_glx_put_texture:
535  * @window: a #GstVaapiWindowGLX
536  * @texture: a #GstVaapiTexture
537  * @src_rect: the sub-rectangle of the source texture to
538  *   extract and process. If %NULL, the entire texture will be used.
539  * @dst_rect: the sub-rectangle of the destination
540  *   window into which the texture is rendered. If %NULL, the entire
541  *   window will be used.
542  *
543  * Renders the @texture region specified by @src_rect into the @window
544  * region specified by @dst_rect.
545  *
546  * NOTE: only GL_TEXTURE_2D textures are supported at this time.
547  *
548  * Return value: %TRUE on success
549  */
550 gboolean
551 gst_vaapi_window_glx_put_texture(
552     GstVaapiWindowGLX       *window,
553     GstVaapiTexture         *texture,
554     const GstVaapiRectangle *src_rect,
555     const GstVaapiRectangle *dst_rect
556 )
557 {
558     GstVaapiRectangle tmp_src_rect, tmp_dst_rect;
559     GLTextureState ts;
560     GLenum tex_target;
561     GLuint tex_id;
562     guint tex_width, tex_height;
563     guint win_width, win_height;
564
565     g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), FALSE);
566     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), FALSE);
567
568     gst_vaapi_texture_get_size(texture, &tex_width, &tex_height);
569     fill_rect(&tmp_src_rect, src_rect, tex_width, tex_height);
570     src_rect = &tmp_src_rect;
571
572     gst_vaapi_window_get_size(GST_VAAPI_WINDOW(window), &win_width, &win_height);
573     fill_rect(&tmp_dst_rect, dst_rect, win_width, win_height);
574     dst_rect = &tmp_dst_rect;
575
576     /* XXX: only GL_TEXTURE_2D textures are supported at this time */
577     tex_target = gst_vaapi_texture_get_target(texture);
578     if (tex_target != GL_TEXTURE_2D)
579         return FALSE;
580
581     tex_id = gst_vaapi_texture_get_id(texture);
582     if (!gl_bind_texture(&ts, tex_target, tex_id))
583         return FALSE;
584     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
585     glPushMatrix();
586     glTranslatef((GLfloat)dst_rect->x, (GLfloat)dst_rect->y, 0.0f);
587     glBegin(GL_QUADS);
588     {
589         const float tx1 = (float)src_rect->x / tex_width;
590         const float tx2 = (float)(src_rect->x + src_rect->width) / tex_width;
591         const float ty1 = (float)src_rect->y / tex_height;
592         const float ty2 = (float)(src_rect->y + src_rect->height) / tex_height;
593         const guint w   = dst_rect->width;
594         const guint h   = dst_rect->height;
595         glTexCoord2f(tx1, ty1); glVertex2i(0, 0);
596         glTexCoord2f(tx1, ty2); glVertex2i(0, h);
597         glTexCoord2f(tx2, ty2); glVertex2i(w, h);
598         glTexCoord2f(tx2, ty1); glVertex2i(w, 0);
599     }
600     glEnd();
601     glPopMatrix();
602     gl_unbind_texture(&ts);
603     return TRUE;
604 }