mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 03:19:40 +00:00
6a465ae793
Add support for GstVideoGLTextureOrientation modes. In particular, add orientation flags to the GstVaapiTexture wrapper and the GLX implementations. Default mode is that texture memory is laid out with top lines first, left row first. Flags indicate whether the X or Y axis need to be inverted.
392 lines
12 KiB
C
392 lines
12 KiB
C
/*
|
|
* gstvaapitexture_glx.c - VA/GLX texture abstraction
|
|
*
|
|
* Copyright (C) 2010-2011 Splitted-Desktop Systems
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
|
|
* Copyright (C) 2012-2014 Intel Corporation
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstvaapitexture_glx
|
|
* @short_description: VA/GLX texture abstraction
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include "gstvaapitexture.h"
|
|
#include "gstvaapitexture_glx.h"
|
|
#include "gstvaapitexture_priv.h"
|
|
#include "gstvaapicompat.h"
|
|
#include "gstvaapiutils.h"
|
|
#include "gstvaapiutils_glx.h"
|
|
#include "gstvaapidisplay_glx.h"
|
|
#include "gstvaapidisplay_x11_priv.h"
|
|
#include "gstvaapidisplay_glx_priv.h"
|
|
|
|
#define DEBUG 1
|
|
#include "gstvaapidebug.h"
|
|
|
|
#define GST_VAAPI_TEXTURE_GLX(texture) \
|
|
((GstVaapiTextureGLX *)(texture))
|
|
|
|
typedef struct _GstVaapiTextureGLX GstVaapiTextureGLX;
|
|
typedef struct _GstVaapiTextureGLXClass GstVaapiTextureGLXClass;
|
|
|
|
/**
|
|
* GstVaapiTextureGLX:
|
|
*
|
|
* Base object for GLX texture wrapper.
|
|
*/
|
|
struct _GstVaapiTextureGLX
|
|
{
|
|
/*< private >*/
|
|
GstVaapiTexture parent_instance;
|
|
|
|
GLContextState *gl_context;
|
|
GLPixmapObject *pixo;
|
|
GLFramebufferObject *fbo;
|
|
};
|
|
|
|
/**
|
|
* GstVaapiTextureGLXClass:
|
|
*
|
|
* Base class for GLX texture wrapper.
|
|
*/
|
|
struct _GstVaapiTextureGLXClass
|
|
{
|
|
/*< private >*/
|
|
GstVaapiTextureClass parent_class;
|
|
};
|
|
|
|
static gboolean
|
|
gst_vaapi_texture_glx_put_surface (GstVaapiTexture * texture,
|
|
GstVaapiSurface * surface, const GstVaapiRectangle * crop_rect,
|
|
guint flags);
|
|
|
|
static void
|
|
destroy_objects (GstVaapiTextureGLX * texture)
|
|
{
|
|
GLContextState old_cs;
|
|
|
|
if (texture->gl_context)
|
|
gl_set_current_context (texture->gl_context, &old_cs);
|
|
|
|
if (texture->fbo) {
|
|
gl_destroy_framebuffer_object (texture->fbo);
|
|
texture->fbo = NULL;
|
|
}
|
|
|
|
if (texture->pixo) {
|
|
gl_destroy_pixmap_object (texture->pixo);
|
|
texture->pixo = NULL;
|
|
}
|
|
|
|
if (texture->gl_context) {
|
|
gl_set_current_context (&old_cs, NULL);
|
|
gl_destroy_context (texture->gl_context);
|
|
texture->gl_context = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
destroy_texture_unlocked (GstVaapiTexture * texture)
|
|
{
|
|
const guint texture_id = GST_VAAPI_TEXTURE_ID (texture);
|
|
|
|
destroy_objects (GST_VAAPI_TEXTURE_GLX (texture));
|
|
|
|
if (texture_id) {
|
|
if (!texture->is_wrapped)
|
|
glDeleteTextures (1, &texture_id);
|
|
GST_VAAPI_TEXTURE_ID (texture) = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_texture_glx_destroy (GstVaapiTexture * texture)
|
|
{
|
|
GST_VAAPI_OBJECT_LOCK_DISPLAY (texture);
|
|
destroy_texture_unlocked (texture);
|
|
GST_VAAPI_OBJECT_UNLOCK_DISPLAY (texture);
|
|
}
|
|
|
|
static gboolean
|
|
create_objects (GstVaapiTextureGLX * texture, guint texture_id)
|
|
{
|
|
GstVaapiTexture *const base_texture = GST_VAAPI_TEXTURE (texture);
|
|
Display *const dpy = GST_VAAPI_OBJECT_NATIVE_DISPLAY (texture);
|
|
GLContextState old_cs;
|
|
gboolean success = FALSE;
|
|
|
|
gl_get_current_context (&old_cs);
|
|
|
|
texture->gl_context = gl_create_context (dpy, DefaultScreen (dpy), &old_cs);
|
|
if (!texture->gl_context ||
|
|
!gl_set_current_context (texture->gl_context, NULL))
|
|
return FALSE;
|
|
|
|
texture->pixo = gl_create_pixmap_object (dpy,
|
|
base_texture->width, base_texture->height);
|
|
if (!texture->pixo) {
|
|
GST_ERROR ("failed to create GLX pixmap");
|
|
goto out_reset_context;
|
|
}
|
|
|
|
texture->fbo = gl_create_framebuffer_object (base_texture->gl_target,
|
|
texture_id, base_texture->width, base_texture->height);
|
|
if (!texture->fbo) {
|
|
GST_ERROR ("failed to create FBO");
|
|
goto out_reset_context;
|
|
}
|
|
success = TRUE;
|
|
|
|
out_reset_context:
|
|
gl_set_current_context (&old_cs, NULL);
|
|
return success;
|
|
}
|
|
|
|
static gboolean
|
|
create_texture_unlocked (GstVaapiTexture * texture)
|
|
{
|
|
guint texture_id;
|
|
|
|
if (texture->is_wrapped)
|
|
texture_id = GST_VAAPI_TEXTURE_ID (texture);
|
|
else {
|
|
texture_id = gl_create_texture (texture->gl_target, texture->gl_format,
|
|
texture->width, texture->height);
|
|
if (!texture_id)
|
|
return FALSE;
|
|
GST_VAAPI_TEXTURE_ID (texture) = texture_id;
|
|
}
|
|
return create_objects (GST_VAAPI_TEXTURE_GLX (texture), texture_id);
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_texture_glx_create (GstVaapiTexture * texture)
|
|
{
|
|
gboolean success;
|
|
|
|
GST_VAAPI_OBJECT_LOCK_DISPLAY (texture);
|
|
success = create_texture_unlocked (texture);
|
|
GST_VAAPI_OBJECT_UNLOCK_DISPLAY (texture);
|
|
return success;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_texture_glx_class_init (GstVaapiTextureGLXClass * klass)
|
|
{
|
|
GstVaapiObjectClass *const object_class = GST_VAAPI_OBJECT_CLASS (klass);
|
|
GstVaapiTextureClass *const texture_class = GST_VAAPI_TEXTURE_CLASS (klass);
|
|
|
|
object_class->finalize = (GstVaapiObjectFinalizeFunc)
|
|
gst_vaapi_texture_glx_destroy;
|
|
|
|
texture_class->allocate = gst_vaapi_texture_glx_create;
|
|
texture_class->put_surface = gst_vaapi_texture_glx_put_surface;
|
|
}
|
|
|
|
#define gst_vaapi_texture_glx_finalize gst_vaapi_texture_glx_destroy
|
|
GST_VAAPI_OBJECT_DEFINE_CLASS_WITH_CODE (GstVaapiTextureGLX,
|
|
gst_vaapi_texture_glx, gst_vaapi_texture_glx_class_init (&g_class));
|
|
|
|
/**
|
|
* gst_vaapi_texture_glx_new:
|
|
* @display: a #GstVaapiDisplay
|
|
* @target: the target to which the texture is bound
|
|
* @format: the format of the pixel data
|
|
* @width: the requested width, in pixels
|
|
* @height: the requested height, in pixels
|
|
*
|
|
* Creates a texture with the specified dimensions, @target and
|
|
* @format. Note that only GL_TEXTURE_2D @target and GL_RGBA or
|
|
* GL_BGRA formats are supported at this time.
|
|
*
|
|
* The application shall maintain the live GL context itself. That is,
|
|
* gst_vaapi_window_glx_make_current() must be called beforehand, or
|
|
* any other function like glXMakeCurrent() if the context is managed
|
|
* outside of this library.
|
|
*
|
|
* Return value: the newly created #GstVaapiTexture object
|
|
*/
|
|
GstVaapiTexture *
|
|
gst_vaapi_texture_glx_new (GstVaapiDisplay * display, guint target,
|
|
guint format, guint width, guint height)
|
|
{
|
|
g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_GLX (display), NULL);
|
|
|
|
return gst_vaapi_texture_new_internal (GST_VAAPI_TEXTURE_CLASS
|
|
(gst_vaapi_texture_glx_class ()), display, GST_VAAPI_ID_INVALID, target,
|
|
format, width, height);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_texture_glx_new_wrapped:
|
|
* @display: a #GstVaapiDisplay
|
|
* @texture_id: the foreign GL texture name to use
|
|
* @target: the target to which the texture is bound
|
|
* @format: the format of the pixel data
|
|
*
|
|
* Creates a texture from an existing GL texture, with the specified
|
|
* @target and @format. Note that only GL_TEXTURE_2D @target and
|
|
* GL_RGBA or GL_BGRA formats are supported at this time. The
|
|
* dimensions will be retrieved from the @texture_id.
|
|
*
|
|
* The application shall maintain the live GL context itself. That is,
|
|
* gst_vaapi_window_glx_make_current() must be called beforehand, or
|
|
* any other function like glXMakeCurrent() if the context is managed
|
|
* outside of this library.
|
|
*
|
|
* Return value: the newly created #GstVaapiTexture object
|
|
*/
|
|
GstVaapiTexture *
|
|
gst_vaapi_texture_glx_new_wrapped (GstVaapiDisplay * display,
|
|
guint texture_id, guint target, guint format)
|
|
{
|
|
guint width, height, border_width;
|
|
GLTextureState ts;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_GLX (display), NULL);
|
|
g_return_val_if_fail (texture_id != GL_NONE, NULL);
|
|
g_return_val_if_fail (target != GL_NONE, NULL);
|
|
g_return_val_if_fail (format != GL_NONE, NULL);
|
|
|
|
/* Check texture dimensions */
|
|
GST_VAAPI_DISPLAY_LOCK (display);
|
|
success = gl_bind_texture (&ts, target, texture_id);
|
|
if (success) {
|
|
if (!gl_get_texture_param (target, GL_TEXTURE_WIDTH, &width) ||
|
|
!gl_get_texture_param (target, GL_TEXTURE_HEIGHT, &height) ||
|
|
!gl_get_texture_param (target, GL_TEXTURE_BORDER, &border_width))
|
|
success = FALSE;
|
|
gl_unbind_texture (&ts);
|
|
}
|
|
GST_VAAPI_DISPLAY_UNLOCK (display);
|
|
if (!success)
|
|
return NULL;
|
|
|
|
width -= 2 * border_width;
|
|
height -= 2 * border_width;
|
|
g_return_val_if_fail (width > 0, NULL);
|
|
g_return_val_if_fail (height > 0, NULL);
|
|
|
|
return gst_vaapi_texture_new_internal (GST_VAAPI_TEXTURE_CLASS
|
|
(gst_vaapi_texture_glx_class ()), display, texture_id, target, format,
|
|
width, height);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_texture_put_surface:
|
|
* @texture: a #GstVaapiTexture
|
|
* @surface: a #GstVaapiSurface
|
|
* @flags: postprocessing flags. See #GstVaapiTextureRenderFlags
|
|
*
|
|
* Renders the @surface into the àtexture. The @flags specify how
|
|
* de-interlacing (if needed), color space conversion, scaling and
|
|
* other postprocessing transformations are performed.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
static gboolean
|
|
gst_vaapi_texture_glx_put_surface_unlocked (GstVaapiTexture * base_texture,
|
|
GstVaapiSurface * surface, const GstVaapiRectangle * crop_rect, guint flags)
|
|
{
|
|
GstVaapiTextureGLX *const texture = GST_VAAPI_TEXTURE_GLX (base_texture);
|
|
VAStatus status;
|
|
GLContextState old_cs;
|
|
gboolean success = FALSE;
|
|
|
|
const GLfloat *txc, *tyc;
|
|
static const GLfloat g_texcoords[2][2] = {
|
|
{0.0f, 1.0f},
|
|
{1.0f, 0.0f},
|
|
};
|
|
|
|
status = vaPutSurface (GST_VAAPI_OBJECT_VADISPLAY (texture),
|
|
GST_VAAPI_OBJECT_ID (surface), texture->pixo->pixmap,
|
|
crop_rect->x, crop_rect->y, crop_rect->width, crop_rect->height,
|
|
0, 0, base_texture->width, base_texture->height,
|
|
NULL, 0, from_GstVaapiSurfaceRenderFlags (flags));
|
|
if (!vaapi_check_status (status, "vaPutSurface() [TFP]"))
|
|
return FALSE;
|
|
|
|
if (texture->gl_context &&
|
|
!gl_set_current_context (texture->gl_context, &old_cs))
|
|
return FALSE;
|
|
|
|
if (!gl_bind_framebuffer_object (texture->fbo)) {
|
|
GST_ERROR ("failed to bind FBO");
|
|
goto out_reset_context;
|
|
}
|
|
|
|
if (!gst_vaapi_surface_sync (surface)) {
|
|
GST_ERROR ("failed to render surface to pixmap");
|
|
goto out_unbind_fbo;
|
|
}
|
|
|
|
if (!gl_bind_pixmap_object (texture->pixo)) {
|
|
GST_ERROR ("could not bind GLX pixmap");
|
|
goto out_unbind_fbo;
|
|
}
|
|
|
|
flags = GST_VAAPI_TEXTURE_FLAGS (texture);
|
|
txc = g_texcoords[! !(flags & GST_VAAPI_TEXTURE_ORIENTATION_FLAG_X_INVERTED)];
|
|
tyc = g_texcoords[! !(flags & GST_VAAPI_TEXTURE_ORIENTATION_FLAG_Y_INVERTED)];
|
|
|
|
glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
|
|
glBegin (GL_QUADS);
|
|
{
|
|
glTexCoord2f (txc[0], tyc[0]);
|
|
glVertex2i (0, 0);
|
|
glTexCoord2f (txc[0], tyc[1]);
|
|
glVertex2i (0, base_texture->height);
|
|
glTexCoord2f (txc[1], tyc[1]);
|
|
glVertex2i (base_texture->width, base_texture->height);
|
|
glTexCoord2f (txc[1], tyc[0]);
|
|
glVertex2i (base_texture->width, 0);
|
|
}
|
|
glEnd ();
|
|
|
|
if (!gl_unbind_pixmap_object (texture->pixo)) {
|
|
GST_ERROR ("failed to release GLX pixmap");
|
|
goto out_unbind_fbo;
|
|
}
|
|
success = TRUE;
|
|
|
|
out_unbind_fbo:
|
|
if (!gl_unbind_framebuffer_object (texture->fbo))
|
|
success = FALSE;
|
|
out_reset_context:
|
|
if (texture->gl_context && !gl_set_current_context (&old_cs, NULL))
|
|
success = FALSE;
|
|
return success;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_texture_glx_put_surface (GstVaapiTexture * texture,
|
|
GstVaapiSurface * surface, const GstVaapiRectangle * crop_rect, guint flags)
|
|
{
|
|
gboolean success;
|
|
|
|
GST_VAAPI_OBJECT_LOCK_DISPLAY (texture);
|
|
success = gst_vaapi_texture_glx_put_surface_unlocked (texture, surface,
|
|
crop_rect, flags);
|
|
GST_VAAPI_OBJECT_UNLOCK_DISPLAY (texture);
|
|
return success;
|
|
}
|