2 * gstvaapiwindow_glx.c - VA/GLX window abstraction
4 * Copyright (C) 2010-2011 Splitted-Desktop Systems
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.
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.
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
23 * SECTION:gstvaapiwindow_glx
24 * @short_description: VA/GLX window abstraction
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"
36 #include "gstvaapidebug.h"
38 G_DEFINE_TYPE(GstVaapiWindowGLX,
40 GST_VAAPI_TYPE_WINDOW_X11);
42 #define GST_VAAPI_WINDOW_GLX_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
44 GST_VAAPI_TYPE_WINDOW_GLX, \
45 GstVaapiWindowGLXPrivate))
47 struct _GstVaapiWindowGLXPrivate {
49 GLContextState *gl_context;
50 guint is_constructed : 1;
51 guint foreign_window : 1;
60 /* Fill rectangle coords with capped bounds */
63 GstVaapiRectangle *dst_rect,
64 const GstVaapiRectangle *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;
75 dst_rect->width = width - dst_rect->x;
76 if (src_rect->y + src_rect->height < height)
77 dst_rect->height = src_rect->height;
79 dst_rect->height = height - dst_rect->y;
84 dst_rect->width = width;
85 dst_rect->height = height;
90 _gst_vaapi_window_glx_destroy_context(GstVaapiWindowGLX *window)
92 GstVaapiWindowGLXPrivate * const priv = window->priv;
94 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
95 if (priv->gl_context) {
96 gl_destroy_context(priv->gl_context);
97 priv->gl_context = NULL;
99 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
103 _gst_vaapi_window_glx_create_context(
104 GstVaapiWindowGLX *window,
105 GLXContext foreign_context
108 GstVaapiWindowGLXPrivate * const priv = window->priv;
109 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
110 GLContextState parent_cs;
112 parent_cs.display = dpy;
113 parent_cs.window = None;
114 parent_cs.context = foreign_context;
116 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
117 priv->gl_context = gl_create_context(
119 GST_VAAPI_OBJECT_XSCREEN(window),
122 if (!priv->gl_context) {
123 GST_DEBUG("could not create GLX context");
127 if (!glXIsDirect(dpy, priv->gl_context->context)) {
128 GST_DEBUG("could not create a direct-rendering GLX context");
129 goto out_destroy_context;
134 gl_destroy_context(priv->gl_context);
135 priv->gl_context = NULL;
137 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
138 return priv->gl_context != NULL;
142 _gst_vaapi_window_glx_ensure_context(
143 GstVaapiWindowGLX *window,
144 GLXContext foreign_context
147 GstVaapiWindowGLXPrivate * const priv = window->priv;
149 if (priv->gl_context) {
150 if (!foreign_context || foreign_context == priv->gl_context->context)
152 _gst_vaapi_window_glx_destroy_context(window);
154 return _gst_vaapi_window_glx_create_context(window, foreign_context);
158 gst_vaapi_window_glx_ensure_context(
159 GstVaapiWindowGLX *window,
160 GLXContext foreign_context
163 GstVaapiWindowGLXPrivate * const priv = window->priv;
164 GLContextState old_cs;
167 if (!_gst_vaapi_window_glx_ensure_context(window, foreign_context))
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");
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);
182 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
184 gst_vaapi_window_get_size(GST_VAAPI_WINDOW(window), &width, &height);
185 gl_resize(width, height);
188 glClear(GL_COLOR_BUFFER_BIT);
189 gl_set_current_context(&old_cs, NULL);
194 gst_vaapi_window_glx_get_visual(GstVaapiWindow *window)
196 GstVaapiWindowGLX * const glx_window = GST_VAAPI_WINDOW_GLX(window);
198 if (!_gst_vaapi_window_glx_ensure_context(glx_window, NULL))
200 return glx_window->priv->gl_context->visual->visual;
204 gst_vaapi_window_glx_destroy_colormap(GstVaapiWindowGLX *window)
206 GstVaapiWindowGLXPrivate * const priv = window->priv;
207 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
210 if (!priv->foreign_window) {
211 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
212 XFreeColormap(dpy, priv->cmap);
213 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
220 gst_vaapi_window_glx_create_colormap(GstVaapiWindowGLX *window)
222 GstVaapiWindowGLXPrivate * const priv = window->priv;
223 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
225 XWindowAttributes wattr;
226 gboolean success = FALSE;
229 if (!priv->foreign_window) {
230 if (!_gst_vaapi_window_glx_ensure_context(window, NULL))
232 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
234 /* XXX: add a GstVaapiDisplayX11:x11-screen property? */
235 screen = GST_VAAPI_OBJECT_XSCREEN(window);
236 priv->cmap = XCreateColormap(
238 RootWindow(dpy, screen),
239 priv->gl_context->visual->visual,
242 success = x11_untrap_errors() == 0;
243 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
246 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
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);
260 gst_vaapi_window_glx_get_colormap(GstVaapiWindow *window)
262 return gst_vaapi_window_glx_create_colormap(GST_VAAPI_WINDOW_GLX(window));
266 gst_vaapi_window_glx_resize(GstVaapiWindow *window, guint width, guint height)
268 GstVaapiWindowGLXPrivate * const priv = GST_VAAPI_WINDOW_GLX(window)->priv;
269 Display * const dpy = GST_VAAPI_OBJECT_XDISPLAY(window);
270 GLContextState old_cs;
272 if (!GST_VAAPI_WINDOW_CLASS(gst_vaapi_window_glx_parent_class)->
273 resize(window, width, height))
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);
282 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
287 gst_vaapi_window_glx_finalize(GObject *object)
289 GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(object);
291 _gst_vaapi_window_glx_destroy_context(window);
292 gst_vaapi_window_glx_destroy_colormap(window);
294 G_OBJECT_CLASS(gst_vaapi_window_glx_parent_class)->finalize(object);
298 gst_vaapi_window_glx_set_property(
305 GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(object);
308 case PROP_GLX_CONTEXT:
309 gst_vaapi_window_glx_set_context(window, g_value_get_pointer(value));
312 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
318 gst_vaapi_window_glx_get_property(
325 GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(object);
328 case PROP_GLX_CONTEXT:
329 g_value_set_pointer(value, gst_vaapi_window_glx_get_context(window));
332 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
338 gst_vaapi_window_glx_constructed(GObject *object)
340 GstVaapiWindowGLXPrivate * const priv = GST_VAAPI_WINDOW_GLX(object)->priv;
341 GObjectClass *parent_class;
343 parent_class = G_OBJECT_CLASS(gst_vaapi_window_glx_parent_class);
344 if (parent_class->constructed)
345 parent_class->constructed(object);
347 priv->foreign_window =
348 gst_vaapi_window_x11_is_foreign_xid(GST_VAAPI_WINDOW_X11(object));
350 priv->is_constructed =
351 gst_vaapi_window_glx_ensure_context(GST_VAAPI_WINDOW_GLX(object), NULL);
355 gst_vaapi_window_glx_class_init(GstVaapiWindowGLXClass *klass)
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);
361 g_type_class_add_private(klass, sizeof(GstVaapiWindowGLXPrivate));
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;
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;
373 * GstVaapiDisplayGLX:glx-context:
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().
378 g_object_class_install_property
381 g_param_spec_pointer("glx-context",
388 gst_vaapi_window_glx_init(GstVaapiWindowGLX *window)
390 GstVaapiWindowGLXPrivate *priv = GST_VAAPI_WINDOW_GLX_GET_PRIVATE(window);
394 priv->gl_context = NULL;
395 priv->is_constructed = FALSE;
396 priv->foreign_window = FALSE;
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
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.
409 * Return value: the newly allocated #GstVaapiWindow object
412 gst_vaapi_window_glx_new(GstVaapiDisplay *display, guint width, guint height)
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);
418 return g_object_new(GST_VAAPI_TYPE_WINDOW_GLX,
420 "id", GST_VAAPI_ID(None),
427 * gst_vaapi_window_glx_new_with_xid:
428 * @display: a #GstVaapiDisplay
429 * @xid: an X11 #Window id
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.
436 * Return value: the newly allocated #GstVaapiWindow object
439 gst_vaapi_window_glx_new_with_xid(GstVaapiDisplay *display, Window xid)
441 GST_DEBUG("new window from xid 0x%08x", xid);
443 g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
444 g_return_val_if_fail(xid != None, NULL);
446 return g_object_new(GST_VAAPI_TYPE_WINDOW_GLX,
448 "id", GST_VAAPI_ID(xid),
453 * gst_vaapi_window_glx_get_context:
454 * @window: a #GstVaapiWindowGLX
456 * Returns the #GLXContext bound to the @window.
458 * Return value: the #GLXContext bound to the @window
461 gst_vaapi_window_glx_get_context(GstVaapiWindowGLX *window)
463 g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), NULL);
464 g_return_val_if_fail(window->priv->is_constructed, FALSE);
466 return window->priv->gl_context->context;
470 * gst_vaapi_window_glx_set_context:
471 * @window: a #GstVaapiWindowGLX
472 * @ctx: a GLX context
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.
479 * Return value: %TRUE on success
482 gst_vaapi_window_glx_set_context(GstVaapiWindowGLX *window, GLXContext ctx)
484 g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), FALSE);
485 g_return_val_if_fail(window->priv->is_constructed, FALSE);
487 return gst_vaapi_window_glx_ensure_context(window, ctx);
491 * gst_vaapi_window_glx_make_current:
492 * @window: a #GstVaapiWindowGLX
494 * Makes the @window GLX context the current GLX rendering context of
495 * the calling thread, replacing the previously current context if
498 * Return value: %TRUE on success
501 gst_vaapi_window_glx_make_current(GstVaapiWindowGLX *window)
505 g_return_val_if_fail(GST_VAAPI_IS_WINDOW_GLX(window), FALSE);
506 g_return_val_if_fail(window->priv->is_constructed, FALSE);
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);
515 * gst_vaapi_window_glx_swap_buffers:
516 * @window: a #GstVaapiWindowGLX
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().
523 gst_vaapi_window_glx_swap_buffers(GstVaapiWindowGLX *window)
525 g_return_if_fail(GST_VAAPI_IS_WINDOW_GLX(window));
526 g_return_if_fail(window->priv->is_constructed);
528 GST_VAAPI_OBJECT_LOCK_DISPLAY(window);
529 gl_swap_buffers(window->priv->gl_context);
530 GST_VAAPI_OBJECT_UNLOCK_DISPLAY(window);
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.
543 * Renders the @texture region specified by @src_rect into the @window
544 * region specified by @dst_rect.
546 * NOTE: only GL_TEXTURE_2D textures are supported at this time.
548 * Return value: %TRUE on success
551 gst_vaapi_window_glx_put_texture(
552 GstVaapiWindowGLX *window,
553 GstVaapiTexture *texture,
554 const GstVaapiRectangle *src_rect,
555 const GstVaapiRectangle *dst_rect
558 GstVaapiRectangle tmp_src_rect, tmp_dst_rect;
562 guint tex_width, tex_height;
563 guint win_width, win_height;
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);
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;
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;
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)
581 tex_id = gst_vaapi_texture_get_id(texture);
582 if (!gl_bind_texture(&ts, tex_target, tex_id))
584 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
586 glTranslatef((GLfloat)dst_rect->x, (GLfloat)dst_rect->y, 0.0f);
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);
602 gl_unbind_texture(&ts);