encoder(h264,h263,mpeg4): changed styles, names
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapitexture.c
1 /*
2  *  gstvaapitexture.c - VA texture 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:gstvaapitexture
24  * @short_description: VA/GLX texture abstraction
25  */
26
27 #include "sysdeps.h"
28 #include "gstvaapitexture.h"
29 #include "gstvaapicompat.h"
30 #include "gstvaapiutils.h"
31 #include "gstvaapiutils_glx.h"
32 #include "gstvaapidisplay_glx.h"
33 #include "gstvaapi_priv.h"
34 #include "gstvaapidisplay_x11_priv.h"
35
36 #define DEBUG 1
37 #include "gstvaapidebug.h"
38
39 G_DEFINE_TYPE(GstVaapiTexture, gst_vaapi_texture, GST_VAAPI_TYPE_OBJECT)
40
41 #define GST_VAAPI_TEXTURE_GET_PRIVATE(obj)                      \
42     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
43                                  GST_VAAPI_TYPE_TEXTURE,        \
44                                  GstVaapiTexturePrivate))
45
46 struct _GstVaapiTexturePrivate {
47     GLenum               target;
48     GLenum               format;
49     guint                width;
50     guint                height;
51     GLContextState      *gl_context;
52     void                *gl_surface;
53     GLPixmapObject      *pixo;
54     GLFramebufferObject *fbo;
55     guint                foreign_texture : 1;
56     guint                is_constructed  : 1;
57 };
58
59 enum {
60     PROP_0,
61
62     PROP_TARGET,
63     PROP_FORMAT,
64     PROP_WIDTH,
65     PROP_HEIGHT
66 };
67
68 static void
69 _gst_vaapi_texture_destroy_objects(GstVaapiTexture *texture)
70 {
71     GstVaapiTexturePrivate * const priv = texture->priv;
72
73 #if USE_VAAPI_GLX
74     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
75     if (priv->gl_surface) {
76         vaDestroySurfaceGLX(
77             GST_VAAPI_OBJECT_VADISPLAY(texture),
78             priv->gl_surface
79         );
80         priv->gl_surface = NULL;
81     }
82     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
83 #else
84     GLContextState old_cs;
85
86     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
87     if (priv->gl_context)
88         gl_set_current_context(priv->gl_context, &old_cs);
89
90     if (priv->fbo) {
91         gl_destroy_framebuffer_object(priv->fbo);
92         priv->fbo = NULL;
93     }
94
95     if (priv->pixo) {
96         gl_destroy_pixmap_object(priv->pixo);
97         priv->pixo = NULL;
98     }
99
100     if (priv->gl_context) {
101         gl_set_current_context(&old_cs, NULL);
102         gl_destroy_context(priv->gl_context);
103         priv->gl_context = NULL;
104     }
105     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
106 #endif
107 }
108
109 static void
110 gst_vaapi_texture_destroy(GstVaapiTexture *texture)
111 {
112     GstVaapiTexturePrivate * const priv = texture->priv;
113     const GLuint texture_id = GST_VAAPI_OBJECT_ID(texture);
114
115     _gst_vaapi_texture_destroy_objects(texture);
116
117     if (texture_id) {
118         if (!priv->foreign_texture)
119             glDeleteTextures(1, &texture_id);
120         GST_VAAPI_OBJECT_ID(texture) = 0;
121     }
122 }
123
124 static gboolean
125 _gst_vaapi_texture_create_objects(GstVaapiTexture *texture, GLuint texture_id)
126 {
127     GstVaapiTexturePrivate * const priv = texture->priv;
128     gboolean success = FALSE;
129
130 #if USE_VAAPI_GLX
131     VAStatus status;
132
133     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
134     status = vaCreateSurfaceGLX(
135         GST_VAAPI_OBJECT_VADISPLAY(texture),
136         priv->target,
137         texture_id,
138         &priv->gl_surface
139     );
140     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
141     success = vaapi_check_status(status, "vaCreateSurfaceGLX()");
142 #else
143     GLContextState old_cs;
144
145     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
146     gl_get_current_context(&old_cs);
147     priv->gl_context = gl_create_context(
148         GST_VAAPI_OBJECT_XDISPLAY(texture),
149         GST_VAAPI_OBJECT_XSCREEN(texture),
150         &old_cs
151     );
152     if (!priv->gl_context || !gl_set_current_context(priv->gl_context, NULL))
153         goto end;
154
155     priv->pixo = gl_create_pixmap_object(
156         GST_VAAPI_OBJECT_XDISPLAY(texture),
157         priv->width,
158         priv->height
159     );
160     if (!priv->pixo)
161         goto end;
162
163     priv->fbo = gl_create_framebuffer_object(
164         priv->target,
165         texture_id,
166         priv->width,
167         priv->height
168     );
169     if (priv->fbo)
170         success = TRUE;
171 end:
172     gl_set_current_context(&old_cs, NULL);
173     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
174 #endif
175     return success;
176 }
177
178 static gboolean
179 gst_vaapi_texture_create(GstVaapiTexture *texture)
180 {
181     GstVaapiTexturePrivate * const priv = texture->priv;
182     GLuint texture_id;
183
184     if (priv->foreign_texture)
185         texture_id = GST_VAAPI_OBJECT_ID(texture);
186     else {
187         GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
188         texture_id = gl_create_texture(
189             priv->target,
190             priv->format,
191             priv->width,
192             priv->height
193         );
194         GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
195         if (!texture_id)
196             return FALSE;
197         GST_VAAPI_OBJECT_ID(texture) = texture_id;
198     }
199
200     return _gst_vaapi_texture_create_objects(texture, texture_id);
201 }
202
203 static void
204 gst_vaapi_texture_finalize(GObject *object)
205 {
206     gst_vaapi_texture_destroy(GST_VAAPI_TEXTURE(object));
207
208     G_OBJECT_CLASS(gst_vaapi_texture_parent_class)->finalize(object);
209 }
210
211 static void
212 gst_vaapi_texture_constructed(GObject *object)
213 {
214     GstVaapiTexture * const texture = GST_VAAPI_TEXTURE(object);
215     GObjectClass *parent_class;
216
217     texture->priv->foreign_texture = GST_VAAPI_OBJECT_ID(texture) != 0;
218     texture->priv->is_constructed  = gst_vaapi_texture_create(texture);
219
220     parent_class = G_OBJECT_CLASS(gst_vaapi_texture_parent_class);
221     if (parent_class->constructed)
222         parent_class->constructed(object);
223 }
224
225 static void
226 gst_vaapi_texture_set_property(
227     GObject      *object,
228     guint         prop_id,
229     const GValue *value,
230     GParamSpec   *pspec
231 )
232 {
233     GstVaapiTexture * const texture = GST_VAAPI_TEXTURE(object);
234
235     switch (prop_id) {
236     case PROP_TARGET:
237         texture->priv->target = g_value_get_uint(value);
238         break;
239     case PROP_FORMAT:
240         texture->priv->format = g_value_get_uint(value);
241         break;
242     case PROP_WIDTH:
243         texture->priv->width = g_value_get_uint(value);
244         break;
245     case PROP_HEIGHT:
246         texture->priv->height = g_value_get_uint(value);
247         break;
248     default:
249         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
250         break;
251     }
252 }
253
254 static void
255 gst_vaapi_texture_get_property(
256     GObject    *object,
257     guint       prop_id,
258     GValue     *value,
259     GParamSpec *pspec
260 )
261 {
262     GstVaapiTexture * const texture = GST_VAAPI_TEXTURE(object);
263
264     switch (prop_id) {
265     case PROP_TARGET:
266         g_value_set_uint(value, gst_vaapi_texture_get_target(texture));
267         break;
268     case PROP_FORMAT:
269         g_value_set_uint(value, gst_vaapi_texture_get_format(texture));
270         break;
271     case PROP_WIDTH:
272         g_value_set_uint(value, gst_vaapi_texture_get_width(texture));
273         break;
274     case PROP_HEIGHT:
275         g_value_set_uint(value, gst_vaapi_texture_get_height(texture));
276         break;
277     default:
278         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
279         break;
280     }
281 }
282
283 static void
284 gst_vaapi_texture_class_init(GstVaapiTextureClass *klass)
285 {
286     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
287
288     g_type_class_add_private(klass, sizeof(GstVaapiTexturePrivate));
289
290     object_class->finalize     = gst_vaapi_texture_finalize;
291     object_class->set_property = gst_vaapi_texture_set_property;
292     object_class->get_property = gst_vaapi_texture_get_property;
293     object_class->constructed  = gst_vaapi_texture_constructed;
294
295     g_object_class_install_property
296         (object_class,
297          PROP_TARGET,
298          g_param_spec_uint("target",
299                            "Target",
300                            "The texture target",
301                            0, G_MAXUINT32, 0,
302                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
303
304     g_object_class_install_property
305         (object_class,
306          PROP_FORMAT,
307          g_param_spec_uint("format",
308                            "Format",
309                            "The texture format",
310                            0, G_MAXUINT32, 0,
311                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
312
313     g_object_class_install_property
314         (object_class,
315          PROP_WIDTH,
316          g_param_spec_uint("width",
317                            "width",
318                            "The texture width",
319                            0, G_MAXUINT32, 0,
320                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
321
322     g_object_class_install_property
323         (object_class,
324          PROP_HEIGHT,
325          g_param_spec_uint("height",
326                            "height",
327                            "The texture height",
328                            0, G_MAXUINT32, 0,
329                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
330 }
331
332 static void
333 gst_vaapi_texture_init(GstVaapiTexture *texture)
334 {
335     GstVaapiTexturePrivate *priv = GST_VAAPI_TEXTURE_GET_PRIVATE(texture);
336
337     texture->priv               = priv;
338     priv->target                = GL_NONE;
339     priv->format                = GL_NONE;
340     priv->width                 = 0;
341     priv->height                = 0;
342     priv->gl_context            = NULL;
343     priv->gl_surface            = NULL;
344     priv->pixo                  = NULL;
345     priv->fbo                   = NULL;
346     priv->foreign_texture       = FALSE;
347     priv->is_constructed        = FALSE;
348 }
349
350 /**
351  * gst_vaapi_texture_new:
352  * @display: a #GstVaapiDisplay
353  * @target: the target to which the texture is bound
354  * @format: the format of the pixel data
355  * @width: the requested width, in pixels
356  * @height: the requested height, in pixels
357  *
358  * Creates a texture with the specified dimensions, @target and
359  * @format. Note that only GL_TEXTURE_2D @target and GL_RGBA or
360  * GL_BGRA formats are supported at this time.
361  *
362  * The application shall maintain the live GL context itself. That is,
363  * gst_vaapi_window_glx_make_current() must be called beforehand, or
364  * any other function like glXMakeCurrent() if the context is managed
365  * outside of this library.
366  *
367  * Return value: the newly created #GstVaapiTexture object
368  */
369 GstVaapiTexture *
370 gst_vaapi_texture_new(
371     GstVaapiDisplay *display,
372     GLenum           target,
373     GLenum           format,
374     guint            width,
375     guint            height
376 )
377 {
378     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
379
380     return g_object_new(GST_VAAPI_TYPE_TEXTURE,
381                         "display", display,
382                         "id",      GST_VAAPI_ID(0),
383                         "target",  target,
384                         "format",  format,
385                         "width",   width,
386                         "height",  height,
387                         NULL);
388 }
389
390 /**
391  * gst_vaapi_texture_new_with_texture:
392  * @display: a #GstVaapiDisplay
393  * @texture: the foreign GL texture name to use
394  * @target: the target to which the texture is bound
395  * @format: the format of the pixel data
396  *
397  * Creates a texture from an existing GL texture, with the specified
398  * @target and @format. Note that only GL_TEXTURE_2D @target and
399  * GL_RGBA or GL_BGRA formats are supported at this time. The
400  * dimensions will be retrieved from the @texture.
401  *
402  * The application shall maintain the live GL context itself. That is,
403  * gst_vaapi_window_glx_make_current() must be called beforehand, or
404  * any other function like glXMakeCurrent() if the context is managed
405  * outside of this library.
406  *
407  * Return value: the newly created #GstVaapiTexture object
408  */
409 GstVaapiTexture *
410 gst_vaapi_texture_new_with_texture(
411     GstVaapiDisplay *display,
412     GLuint           texture,
413     GLenum           target,
414     GLenum           format
415 )
416 {
417     guint width, height, border_width;
418     GLTextureState ts;
419     gboolean success;
420
421     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
422
423     /* Check texture dimensions */
424     GST_VAAPI_DISPLAY_LOCK(display);
425     success = gl_bind_texture(&ts, target, texture);
426     if (success) {
427         if (!gl_get_texture_param(target, GL_TEXTURE_WIDTH,  &width)  ||
428             !gl_get_texture_param(target, GL_TEXTURE_HEIGHT, &height) ||
429             !gl_get_texture_param(target, GL_TEXTURE_BORDER, &border_width))
430             success = FALSE;
431         gl_unbind_texture(&ts);
432     }
433     GST_VAAPI_DISPLAY_UNLOCK(display);
434     if (!success)
435         return NULL;
436
437     width  -= 2 * border_width;
438     height -= 2 * border_width;
439     if (width == 0 || height == 0)
440         return NULL;
441
442     return g_object_new(GST_VAAPI_TYPE_TEXTURE,
443                         "display", display,
444                         "id",      GST_VAAPI_ID(texture),
445                         "target",  target,
446                         "format",  format,
447                         "width",   width,
448                         "height",  height,
449                         NULL);
450 }
451
452 /**
453  * gst_vaapi_texture_get_id:
454  * @texture: a #GstVaapiTexture
455  *
456  * Returns the underlying texture id of the @texture.
457  *
458  * Return value: the underlying texture id of the @texture
459  */
460 GLuint
461 gst_vaapi_texture_get_id(GstVaapiTexture *texture)
462 {
463     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), 0);
464
465     return GST_VAAPI_OBJECT_ID(texture);
466 }
467
468 /**
469  * gst_vaapi_texture_get_target:
470  * @texture: a #GstVaapiTexture
471  *
472  * Returns the @texture target type
473  *
474  * Return value: the texture target
475  */
476 GLenum
477 gst_vaapi_texture_get_target(GstVaapiTexture *texture)
478 {
479     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), GL_NONE);
480     g_return_val_if_fail(texture->priv->is_constructed, GL_NONE);
481
482     return texture->priv->target;
483 }
484
485 /**
486  * gst_vaapi_texture_get_format
487  * @texture: a #GstVaapiTexture
488  *
489  * Returns the @texture format
490  *
491  * Return value: the texture format
492  */
493 GLenum
494 gst_vaapi_texture_get_format(GstVaapiTexture *texture)
495 {
496     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), GL_NONE);
497     g_return_val_if_fail(texture->priv->is_constructed, GL_NONE);
498
499     return texture->priv->format;
500 }
501
502 /**
503  * gst_vaapi_texture_get_width:
504  * @texture: a #GstVaapiTexture
505  *
506  * Returns the @texture width.
507  *
508  * Return value: the texture width, in pixels
509  */
510 guint
511 gst_vaapi_texture_get_width(GstVaapiTexture *texture)
512 {
513     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), 0);
514     g_return_val_if_fail(texture->priv->is_constructed, 0);
515
516     return texture->priv->width;
517 }
518
519 /**
520  * gst_vaapi_texture_get_height:
521  * @texture: a #GstVaapiTexture
522  *
523  * Returns the @texture height.
524  *
525  * Return value: the texture height, in pixels.
526  */
527 guint
528 gst_vaapi_texture_get_height(GstVaapiTexture *texture)
529 {
530     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), 0);
531     g_return_val_if_fail(texture->priv->is_constructed, 0);
532
533     return texture->priv->height;
534 }
535
536 /**
537  * gst_vaapi_texture_get_size:
538  * @texture: a #GstVaapiTexture
539  * @pwidth: return location for the width, or %NULL
540  * @pheight: return location for the height, or %NULL
541  *
542  * Retrieves the dimensions of a #GstVaapiTexture.
543  */
544 void
545 gst_vaapi_texture_get_size(
546     GstVaapiTexture *texture,
547     guint           *pwidth,
548     guint           *pheight
549 )
550 {
551     g_return_if_fail(GST_VAAPI_IS_TEXTURE(texture));
552     g_return_if_fail(texture->priv->is_constructed);
553
554     if (pwidth)
555         *pwidth = texture->priv->width;
556
557     if (pheight)
558         *pheight = texture->priv->height;
559 }
560
561 /**
562  * gst_vaapi_texture_put_surface:
563  * @texture: a #GstVaapiTexture
564  * @surface: a #GstVaapiSurface
565  * @flags: postprocessing flags. See #GstVaapiTextureRenderFlags
566  *
567  * Renders the @surface into the àtexture. The @flags specify how
568  * de-interlacing (if needed), color space conversion, scaling and
569  * other postprocessing transformations are performed.
570  *
571  * Return value: %TRUE on success
572  */
573 static gboolean
574 _gst_vaapi_texture_put_surface(
575     GstVaapiTexture *texture,
576     GstVaapiSurface *surface,
577     guint            flags
578 )
579 {
580     GstVaapiTexturePrivate * const priv = texture->priv;
581     VAStatus status;
582
583 #if USE_VAAPI_GLX
584     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
585     status = vaCopySurfaceGLX(
586         GST_VAAPI_OBJECT_VADISPLAY(texture),
587         priv->gl_surface,
588         GST_VAAPI_OBJECT_ID(surface),
589         from_GstVaapiSurfaceRenderFlags(flags)
590     );
591     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
592     if (!vaapi_check_status(status, "vaCopySurfaceGLX()"))
593         return FALSE;
594 #else
595     guint surface_width, surface_height;
596     GLContextState old_cs;
597     gboolean success = FALSE;
598
599     gst_vaapi_surface_get_size(surface, &surface_width, &surface_height);
600
601     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
602     status = vaPutSurface(
603         GST_VAAPI_OBJECT_VADISPLAY(texture),
604         GST_VAAPI_OBJECT_ID(surface),
605         priv->pixo->pixmap,
606         0, 0, surface_width, surface_height,
607         0, 0, priv->width, priv->height,
608         NULL, 0,
609         from_GstVaapiSurfaceRenderFlags(flags)
610     );
611     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
612     if (!vaapi_check_status(status, "vaPutSurface() [TFP]"))
613         return FALSE;
614
615     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
616     if (priv->gl_context) {
617         success = gl_set_current_context(priv->gl_context, &old_cs);
618         if (!success)
619             goto end;
620     }
621
622     success = gl_bind_framebuffer_object(priv->fbo);
623     if (!success) {
624         GST_DEBUG("could not bind FBO");
625         goto out_reset_context;
626     }
627
628     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
629     success = gst_vaapi_surface_sync(surface);
630     GST_VAAPI_OBJECT_LOCK_DISPLAY(texture);
631     if (!success) {
632         GST_DEBUG("could not render surface to pixmap");
633         goto out_unbind_fbo;
634     }
635
636     success = gl_bind_pixmap_object(priv->pixo);
637     if (!success) {
638         GST_DEBUG("could not bind GLX pixmap");
639         goto out_unbind_fbo;
640     }
641
642     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
643     glBegin(GL_QUADS);
644     {
645         glTexCoord2f(0.0f, 0.0f); glVertex2i(0,           0           );
646         glTexCoord2f(0.0f, 1.0f); glVertex2i(0,           priv->height);
647         glTexCoord2f(1.0f, 1.0f); glVertex2i(priv->width, priv->height);
648         glTexCoord2f(1.0f, 0.0f); glVertex2i(priv->width, 0           );
649     }
650     glEnd();
651
652     success = gl_unbind_pixmap_object(priv->pixo);
653     if (!success) {
654         GST_DEBUG("could not release GLX pixmap");
655         goto out_unbind_fbo;
656     }
657
658 out_unbind_fbo:
659     if (!gl_unbind_framebuffer_object(priv->fbo))
660         success = FALSE;
661 out_reset_context:
662     if (priv->gl_context && !gl_set_current_context(&old_cs, NULL))
663         success = FALSE;
664 end:
665     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(texture);
666     return success;
667 #endif
668     return TRUE;
669 }
670
671 gboolean
672 gst_vaapi_texture_put_surface(
673     GstVaapiTexture *texture,
674     GstVaapiSurface *surface,
675     guint            flags
676 )
677 {
678     g_return_val_if_fail(GST_VAAPI_IS_TEXTURE(texture), FALSE);
679     g_return_val_if_fail(texture->priv->is_constructed, FALSE);
680     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
681
682     return _gst_vaapi_texture_put_surface(texture, surface, flags);
683 }