diff --git a/ext/gl/gstglimagesink.c b/ext/gl/gstglimagesink.c index 144b1aa46d..258a6174fc 100644 --- a/ext/gl/gstglimagesink.c +++ b/ext/gl/gstglimagesink.c @@ -178,10 +178,10 @@ static GstStaticPadTemplate gst_glimage_sink_template = GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, "RGBA") "; " #endif - GST_VIDEO_CAPS_MAKE (GST_GL_UPLOAD_FORMATS) "; " + GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS) "; " GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, - GST_GL_UPLOAD_FORMATS)) + GST_GL_COLOR_CONVERT_FORMATS)) ); enum diff --git a/ext/gl/gstgltestsrc.c b/ext/gl/gstgltestsrc.c index 59cefd77d5..fea9ddfc3a 100644 --- a/ext/gl/gstgltestsrc.c +++ b/ext/gl/gstgltestsrc.c @@ -160,7 +160,7 @@ gst_gl_test_src_class_init (GstGLTestSrcClass * klass) gst_element_class_add_pad_template (element_class, gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - gst_caps_from_string (GST_GL_DOWNLOAD_VIDEO_CAPS))); + gst_caps_from_string (GST_GL_COLOR_CONVERT_VIDEO_CAPS))); element_class->set_context = gst_gl_test_src_set_context; diff --git a/gst-libs/gst/gl/Makefile.am b/gst-libs/gst/gl/Makefile.am index 879425b659..bfda193d7c 100644 --- a/gst-libs/gst/gl/Makefile.am +++ b/gst-libs/gst/gl/Makefile.am @@ -17,6 +17,7 @@ libgstgl_@GST_API_VERSION@_la_SOURCES = \ gstglmixer.c \ gstglshader.c \ gstglshadervariables.c \ + gstglcolorconvert.c \ gstgldownload.c \ gstglupload.c \ gstglwindow.c \ @@ -38,6 +39,7 @@ libgstgl_@GST_API_VERSION@include_HEADERS = \ gstglmixerpad.h \ gstglshadervariables.h \ gstglshader.h \ + gstglcolorconvert.h \ gstgldownload.h \ gstglupload.h \ gstglapi.h \ diff --git a/gst-libs/gst/gl/gl.h b/gst-libs/gst/gl/gl.h index 07d9478120..32cf5fd69c 100644 --- a/gst-libs/gst/gl/gl.h +++ b/gst-libs/gst/gl/gl.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/gst-libs/gst/gl/gstgl_fwd.h b/gst-libs/gst/gl/gstgl_fwd.h index 8de50c2324..4b9a0940e2 100644 --- a/gst-libs/gst/gl/gstgl_fwd.h +++ b/gst-libs/gst/gl/gstgl_fwd.h @@ -55,6 +55,10 @@ typedef struct _GstGLUpload GstGLUpload; typedef struct _GstGLUploadClass GstGLUploadClass; typedef struct _GstGLUploadPrivate GstGLUploadPrivate; +typedef struct _GstGLColorConvert GstGLColorConvert; +typedef struct _GstGLColorConvertClass GstGLColorConvertClass; +typedef struct _GstGLColorConvertPrivate GstGLColorConvertPrivate; + G_END_DECLS #endif /* __GST_GL_FWD_H__ */ diff --git a/gst-libs/gst/gl/gstglcolorconvert.c b/gst-libs/gst/gl/gstglcolorconvert.c new file mode 100644 index 0000000000..82b091eb16 --- /dev/null +++ b/gst-libs/gst/gl/gstglcolorconvert.c @@ -0,0 +1,1251 @@ +/* + * GStreamer + * Copyright (C) 2012-2014 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gl.h" +#include "gstglcolorconvert.h" + +/** + * SECTION:gstglcolorconvert + * @short_description: an object that converts between color spaces/formats + * @see_also: #GstGLUpload, #GstGLDownload, #GstGLMemory + * + * #GstGLColorConvert is an object that converts between color spaces and/or + * formats using OpenGL Shaders. + * + * A #GstGLColorConvert can be created with gst_gl_color_convert_new(). + * + * For handling stride scaling in the shader, see + * gst_gl_color_convert_set_texture_scaling(). + */ + +#define USING_OPENGL(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL) +#define USING_OPENGL3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_OPENGL3) +#define USING_GLES(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES) +#define USING_GLES2(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) +#define USING_GLES3(context) (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES3) + +static void _do_convert (GstGLContext * context, GstGLColorConvert * convert); +static void _init_convert (GstGLContext * context, GstGLColorConvert * convert); +static gboolean _init_convert_fbo (GstGLContext * context, + GstGLColorConvert * convert); +static gboolean _gst_gl_color_convert_perform_unlocked (GstGLColorConvert * + convert, guint in_tex[GST_VIDEO_MAX_PLANES], + guint out_tex[GST_VIDEO_MAX_PLANES]); + +static gboolean _do_convert_draw (GstGLContext * context, + GstGLColorConvert * convert); + +/* *INDENT-OFF* */ + +#define YUV_TO_RGB_COEFFICIENTS \ + "uniform vec3 offset;\n" \ + "uniform vec3 coeff1;\n" \ + "uniform vec3 coeff2;\n" \ + "uniform vec3 coeff3;\n" + +/* FIXME: use the colormatrix support from videoconvert */ + +/* BT. 601 standard with the following ranges: + * Y = [16..235] (of 255) + * Cb/Cr = [16..240] (of 255) + */ +static const gfloat from_yuv_bt601_offset[] = {-0.0625, -0.5, -0.5}; +static const gfloat from_yuv_bt601_rcoeff[] = {1.164, 0.000, 1.596}; +static const gfloat from_yuv_bt601_gcoeff[] = {1.164,-0.391,-0.813}; +static const gfloat from_yuv_bt601_bcoeff[] = {1.164, 2.018, 0.000}; + +/* BT. 709 standard with the following ranges: + * Y = [16..235] (of 255) + * Cb/Cr = [16..240] (of 255) + */ +static const gfloat from_yuv_bt709_offset[] = {-0.0625, -0.5, -0.5}; +static const gfloat from_yuv_bt709_rcoeff[] = {1.164, 0.000, 1.787}; +static const gfloat from_yuv_bt709_gcoeff[] = {1.164,-0.213,-0.531}; +static const gfloat from_yuv_bt709_bcoeff[] = {1.164,-2.112, 0.000}; + +#define RGB_TO_YUV_COEFFICIENTS \ + "uniform vec3 offset;\n" \ + "uniform vec3 coeff1;\n" \ + "uniform vec3 coeff2;\n" \ + "uniform vec3 coeff3;\n" + +/* Matrix inverses of the color matrices found above */ +/* BT. 601 standard with the following ranges: + * Y = [16..235] (of 255) + * Cb/Cr = [16..240] (of 255) + */ +static const gfloat from_rgb_bt601_offset[] = {0.0625, 0.5, 0.5}; +static const gfloat from_rgb_bt601_ycoeff[] = {0.256816, 0.504154, 0.0979137}; +static const gfloat from_rgb_bt601_ucoeff[] = {-0.148246, -0.29102, 0.439266}; +static const gfloat from_rgb_bt601_vcoeff[] = {0.439271, -0.367833, -0.071438}; + +/* BT. 709 standard with the following ranges: + * Y = [16..235] (of 255) + * Cb/Cr = [16..240] (of 255) + */ +static const gfloat from_rgb_bt709_offset[] = {0.0625, 0.5, 0.5}; +static const gfloat from_rgb_bt709_ycoeff[] = {0.213392, 0.718140,-0.072426}; +static const gfloat from_rgb_bt709_ucoeff[] = {0.117608, 0.395793,-0.513401}; +static const gfloat from_rgb_bt709_vcoeff[] = {0.420599,-0.467775, 0.047176}; + +/** GRAY16 to RGB conversion + * data transfered as GL_LUMINANCE_ALPHA then convert back to GRAY16 + * high byte weight as : 255*256/65535 + * ([0~1] denormalize to [0~255],shift to high byte,normalize to [0~1]) + * low byte weight as : 255/65535 (similar) + * */ +#define COMPOSE_WEIGHT \ + "const vec2 compose_weight = vec2(0.996109, 0.003891);\n" + +/* Channel reordering for XYZ <-> ZYX conversion */ +static const char *frag_REORDER = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D tex;\n" + "uniform vec2 tex_scale0;\n" + "uniform vec2 tex_scale1;\n" + "uniform vec2 tex_scale2;\n" + "void main(void)\n" + "{\n" + " vec4 t = texture2D(tex, v_texcoord * tex_scale0);\n" + " gl_FragColor = vec4(t.%c, t.%c, t.%c, t.%c);\n" + "}"; + +/** GRAY16 to RGB conversion + * data transfered as GL_LUMINANCE_ALPHA then convert back to GRAY16 + * high byte weight as : 255*256/65535 + * ([0~1] denormalize to [0~255],shift to high byte,normalize to [0~1]) + * low byte weight as : 255/65535 (similar) + * */ +static const char *frag_COMPOSE = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D tex;\n" + "uniform vec2 tex_scale0;\n" + "uniform vec2 tex_scale1;\n" + "uniform vec2 tex_scale2;\n" + COMPOSE_WEIGHT + "void main(void)\n" + "{\n" + " float r, g, b, a;\n" + " vec4 t = texture2D(tex, v_texcoord * tex_scale0);\n" + " r = dot(t.%c%c, compose_weight);" + " g = r;\n" + " b = r;\n" + " a = 1.0;\n" + " gl_FragColor = vec4(%c, %c, %c, %c);\n" + "}"; + +static const char *frag_AYUV_to_RGB = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D tex;\n" + "uniform vec2 tex_scale0;\n" + "uniform vec2 tex_scale1;\n" + "uniform vec2 tex_scale2;\n" + YUV_TO_RGB_COEFFICIENTS + "void main(void) {\n" + " float r,g,b,a;\n" + " vec3 yuv;\n" + " yuv = texture2D(tex,v_texcoord * tex_scale0).gba;\n" + " yuv += offset;\n" + " r = dot(yuv, coeff1);\n" + " g = dot(yuv, coeff2);\n" + " b = dot(yuv, coeff3);\n" + " a = 1.0;\n" + " gl_FragColor=vec4(%c,%c,%c,%c);\n" + "}"; + +static const gchar *frag_RGB_to_AYUV = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D tex;\n" + RGB_TO_YUV_COEFFICIENTS + "void main(void) {\n" + " vec4 texel;\n" + " float y, u, v;\n" + " texel = texture2D(tex, v_texcoord).%c%c%c%c;\n" + " y = dot(texel.rgb, coeff1);\n" + " u = dot(texel.rgb, coeff2);\n" + " v = dot(texel.rgb, coeff3);\n" + " y += offset.x;\n" + " u += offset.y;\n" + " v += offset.z;\n" + " gl_FragColor = vec4(1.0,y,u,v);\n" + "}\n"; + +/** YUV to RGB conversion */ +static const char *frag_PLANAR_YUV_to_RGB = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D Ytex, Utex, Vtex;\n" + "uniform vec2 tex_scale0;\n" + "uniform vec2 tex_scale1;\n" + "uniform vec2 tex_scale2;\n" + YUV_TO_RGB_COEFFICIENTS + "void main(void) {\n" + " float r, g, b, a;\n" + " vec3 yuv;\n" + " yuv.x = texture2D(Ytex,v_texcoord * tex_scale0).r;\n" + " yuv.y = texture2D(Utex,v_texcoord * tex_scale1).r;\n" + " yuv.z = texture2D(Vtex,v_texcoord * tex_scale2).r;\n" + " yuv += offset;\n" + " r = dot(yuv, coeff1);\n" + " g = dot(yuv, coeff2);\n" + " b = dot(yuv, coeff3);\n" + " a = 1.0;\n" + " gl_FragColor = vec4(%c, %c, %c, %c);\n" + "}\n"; + +static const gchar *frag_RGB_to_PLANAR_YUV = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D tex;\n" + "uniform float w, h;\n" + RGB_TO_YUV_COEFFICIENTS + "void main(void) {\n" + " float y, u, v;\n" + " vec4 texel = texture2D(tex, v_texcoord).%c%c%c%c;\n" + " vec4 texel2 = texture2D(tex, v_texcoord * 2.0).%c%c%c%c;\n" + " y = dot(texel.rgb, coeff1);\n" + " u = dot(texel2.rgb, coeff2);\n" + " v = dot(texel2.rgb, coeff3);\n" + " y += offset.x;\n" + " u += offset.y;\n" + " v += offset.z;\n" + " gl_FragData[0] = vec4(y, 0.0, 0.0, 1.0);\n" + " gl_FragData[1] = vec4(u, 0.0, 0.0, 1.0);\n" + " gl_FragData[2] = vec4(v, 0.0, 0.0, 1.0);\n" + "}\n"; + +/** NV12/NV21 to RGB conversion */ +static const char *frag_NV12_NV21_to_RGB = { + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D Ytex,UVtex;\n" + "uniform vec2 tex_scale0;\n" + "uniform vec2 tex_scale1;\n" + "uniform vec2 tex_scale2;\n" + YUV_TO_RGB_COEFFICIENTS + "void main(void) {\n" + " float r, g, b, a;\n" + " vec3 yuv;\n" + " yuv.x=texture2D(Ytex, v_texcoord * tex_scale0).r;\n" + " yuv.yz=texture2D(UVtex, v_texcoord * tex_scale1).%c%c;\n" + " yuv += offset;\n" + " r = dot(yuv, coeff1);\n" + " g = dot(yuv, coeff2);\n" + " b = dot(yuv, coeff3);\n" + " a = 1.0;\n" + " gl_FragColor=vec4(%c, %c, %c, %c);\n" + "}" +}; + +/* YUY2:r,g,a + UYVY:a,b,r */ +static const gchar *frag_YUY2_UYVY_to_RGB = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D Ytex, UVtex;\n" + "uniform vec2 tex_scale0;\n" + "uniform vec2 tex_scale1;\n" + "uniform vec2 tex_scale2;\n" + YUV_TO_RGB_COEFFICIENTS + "void main(void) {\n" + " vec3 yuv;\n" + " float r, g, b, a;\n" + " yuv.x = texture2D(Ytex, v_texcoord * tex_scale0).%c;\n" + " yuv.yz = texture2D(UVtex, v_texcoord * tex_scale1).%c%c;\n" + " yuv += offset;\n" + " r = dot(yuv, coeff1);\n" + " g = dot(yuv, coeff2);\n" + " b = dot(yuv, coeff3);\n" + " a = 1.0;\n" + " gl_FragColor = vec4(%c, %c, %c, %c);\n" + "}\n"; + +static const gchar *frag_RGB_to_YUY2_UYVY = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D tex;\n" + "uniform float width;\n" + RGB_TO_YUV_COEFFICIENTS + "void main(void) {\n" + " vec4 texel1, texel2;\n" + " float fx, fy, y1, y2, u, v;\n" + " fx = v_texcoord.x;\n" + " fy = v_texcoord.y;\n" + " texel1 = texture2D(tex, vec2(fx*2.0, fy)).%c%c%c%c;\n" + " texel2 = texture2D(tex, vec2(fx*2.0+1.0 / width, fy)).%c%c%c%c;\n" + " y1 = dot(texel1.rgb, coeff1);\n" + " y2 = dot(texel2.rgb, coeff1);\n" + " u = dot(texel1.rgb, coeff2);\n" + " v = dot(texel1.rgb, coeff3);\n" + " y1 += offset.x;\n" + " y2 += offset.x;\n" + " u += offset.y;\n" + " v += offset.z;\n" + " gl_FragColor = vec4(%s);\n" + "}\n"; + +static const gchar *text_vertex_shader = + "attribute vec4 a_position; \n" + "attribute vec2 a_texcoord; \n" + "varying vec2 v_texcoord; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position; \n" + " v_texcoord = a_texcoord; \n" + "} \n"; + +/* *INDENT-ON* */ + +struct ConvertInfo +{ + gint in_n_textures; + gint out_n_textures; + gchar *frag_prog; + const gchar *shader_tex_names[GST_VIDEO_MAX_PLANES]; + gfloat shader_scaling[GST_VIDEO_MAX_PLANES][2]; + gfloat *cms_offset; + gfloat *cms_coeff1; /* r,y */ + gfloat *cms_coeff2; /* g,u */ + gfloat *cms_coeff3; /* b,v */ +}; + +struct _GstGLColorConvertPrivate +{ + int n_textures; + gboolean result; + + gboolean (*draw) (GstGLContext * context, GstGLColorConvert * download); + + struct ConvertInfo convert_info; + + guint tex_id; + gboolean mapped; +}; + +GST_DEBUG_CATEGORY_STATIC (gst_gl_color_convert_debug); +#define GST_CAT_DEFAULT gst_gl_color_convert_debug + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_gl_color_convert_debug, "glconvert", 0, "convert"); + +G_DEFINE_TYPE_WITH_CODE (GstGLColorConvert, gst_gl_color_convert, G_TYPE_OBJECT, + DEBUG_INIT); +static void gst_gl_color_convert_finalize (GObject * object); + +#define GST_GL_COLOR_CONVERT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + GST_TYPE_GL_COLOR_CONVERT, GstGLColorConvertPrivate)) + +static void +gst_gl_color_convert_class_init (GstGLColorConvertClass * klass) +{ + g_type_class_add_private (klass, sizeof (GstGLColorConvertPrivate)); + + G_OBJECT_CLASS (klass)->finalize = gst_gl_color_convert_finalize; +} + +static void +gst_gl_color_convert_init (GstGLColorConvert * convert) +{ + convert->priv = GST_GL_COLOR_CONVERT_GET_PRIVATE (convert); + + g_mutex_init (&convert->lock); +} + +/** + * gst_gl_color_convert_new: + * @context: a #GstGLContext + * + * Returns: a new #GstGLColorConvert object + */ +GstGLColorConvert * +gst_gl_color_convert_new (GstGLContext * context) +{ + GstGLColorConvert *convert; + GstGLColorConvertPrivate *priv; + + convert = g_object_new (GST_TYPE_GL_COLOR_CONVERT, NULL); + + convert->context = gst_object_ref (context); + priv = convert->priv; + + priv->draw = _do_convert_draw; + + return convert; +} + +static void +gst_gl_color_convert_finalize (GObject * object) +{ + GstGLColorConvert *convert; + + convert = GST_GL_COLOR_CONVERT (object); + + if (convert->fbo || convert->depth_buffer) { + gst_gl_context_del_fbo (convert->context, convert->fbo, + convert->depth_buffer); + convert->fbo = 0; + convert->depth_buffer = 0; + } + if (convert->shader) { + gst_object_unref (convert->shader); + convert->shader = NULL; + } + + if (convert->context) { + gst_object_unref (convert->context); + convert->context = NULL; + } + + g_mutex_clear (&convert->lock); + + G_OBJECT_CLASS (gst_gl_color_convert_parent_class)->finalize (object); +} + +static gboolean +_gst_gl_color_convert_init_format_unlocked (GstGLColorConvert * convert, + GstVideoInfo in_info, GstVideoInfo out_info) +{ + g_return_val_if_fail (convert != NULL, FALSE); + g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&in_info) != + GST_VIDEO_FORMAT_UNKNOWN, FALSE); + g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&in_info) != + GST_VIDEO_FORMAT_ENCODED, FALSE); + + if (convert->initted) { + return FALSE; + } else { + convert->initted = TRUE; + } + + convert->in_info = in_info; + convert->out_info = out_info; + + gst_gl_context_thread_add (convert->context, + (GstGLContextThreadFunc) _init_convert, convert); + + return convert->priv->result; +} + +/** + * gst_gl_color_convert_init_format: + * @convert: a #GstGLColorConvert + * @in_info: input #GstVideoInfo + * @out_info: output #GstVideoInfo + * + * Initializes @convert with the information required for conversion. + * + * Returns: whether the initialization was successful + */ +gboolean +gst_gl_color_convert_init_format (GstGLColorConvert * convert, + GstVideoInfo in_info, GstVideoInfo out_info) +{ + gboolean ret; + + g_mutex_lock (&convert->lock); + ret = _gst_gl_color_convert_init_format_unlocked (convert, in_info, out_info); + g_mutex_unlock (&convert->lock); + + return ret; +} + +/** + * gst_gl_color_convert_perform: + * @convert: a #GstGLColorConvert + * @in_tex: the texture ids for input formatted according to in_info + * @out_tex: the texture ids for output formatted according to out_info + * + * Converts the data contained in in_tex into out_tex using the formats + * specified by the #GstVideoInfos passed to + * gst_gl_color_convert_init_format() + * + * Returns: whether the conversion was successful + */ +gboolean +gst_gl_color_convert_perform (GstGLColorConvert * convert, + guint in_tex[GST_VIDEO_MAX_PLANES], guint out_tex[GST_VIDEO_MAX_PLANES]) +{ + gboolean ret; + + g_return_val_if_fail (convert != NULL, FALSE); + + g_mutex_lock (&convert->lock); + ret = _gst_gl_color_convert_perform_unlocked (convert, in_tex, out_tex); + g_mutex_unlock (&convert->lock); + + return ret; +} + +/** + * gst_gl_color_convert_set_texture_scaling: + * @convert: a #GstGLColorConvert + * @scaling: array of texture scaling coefficients stored in width, height + * order per texture being converted. + * + * Scales the input textures by the given amount. Useful for performing stride + * scaling in a shader on OpenGL platforms that do not support + * GL_PIXEL_[UN]PACK_LENGTH such as GL|ES 2.0. + */ +void +gst_gl_color_convert_set_texture_scaling (GstGLColorConvert * convert, + gfloat scaling[GST_VIDEO_MAX_PLANES][2]) +{ + guint i; + + g_return_val_if_fail (convert != NULL, FALSE); + + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + convert->priv->convert_info.shader_scaling[i][0] = scaling[i][0]; + convert->priv->convert_info.shader_scaling[i][1] = scaling[i][1]; + } +} + +static gboolean +_gst_gl_color_convert_perform_unlocked (GstGLColorConvert * convert, + guint in_tex[GST_VIDEO_MAX_PLANES], guint out_tex[GST_VIDEO_MAX_PLANES]) +{ + g_return_val_if_fail (convert != NULL, FALSE); + g_return_val_if_fail (in_tex, FALSE); + g_return_val_if_fail (out_tex, FALSE); + + convert->in_tex[0] = in_tex[0]; + convert->in_tex[1] = in_tex[1]; + convert->in_tex[2] = in_tex[2]; + convert->in_tex[3] = in_tex[3]; + convert->out_tex[0] = out_tex[0]; + convert->out_tex[1] = out_tex[1]; + convert->out_tex[2] = out_tex[2]; + convert->out_tex[3] = out_tex[3]; + + GST_LOG ("Converting %s from %u,%u,%u,%u into %s using %u,%u,%u,%u", + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)), + in_tex[0], in_tex[1], in_tex[2], in_tex[3], + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->out_info)), + out_tex[0], out_tex[1], out_tex[2], out_tex[3]); + + gst_gl_context_thread_add (convert->context, + (GstGLContextThreadFunc) _do_convert, convert); + + return TRUE; +} + +static gboolean +_create_shader (GstGLContext * context, const gchar * vertex_src, + const gchar * fragment_src, GstGLShader ** out_shader) +{ + GstGLShader *shader; + GError *error = NULL; + + g_return_val_if_fail (vertex_src != NULL || fragment_src != NULL, FALSE); + + shader = gst_gl_shader_new (context); + + if (vertex_src) + gst_gl_shader_set_vertex_source (shader, vertex_src); + if (fragment_src) + gst_gl_shader_set_fragment_source (shader, fragment_src); + + if (!gst_gl_shader_compile (shader, &error)) { + gst_gl_context_set_error (context, "%s", error->message); + g_error_free (error); + gst_gl_context_clear_shader (context); + gst_object_unref (shader); + return FALSE; + } + + *out_shader = shader; + return TRUE; +} + +static inline const gchar +_index_to_shader_swizzle (int idx) +{ + switch (idx) { + case 0: + return 'r'; + case 1: + return 'g'; + case 2: + return 'b'; + case 3: + return 'a'; + default: + return '#'; + } +} + +/* attempts to transform expected to want using swizzling */ +static gchar * +_RGB_pixel_order (const gchar * expected, const gchar * wanted) +{ + GString *ret = g_string_sized_new (4); + gchar *expect, *want; + int len; + + if (g_ascii_strcasecmp (expected, wanted) == 0) + return g_ascii_strdown (expected, -1); + + expect = g_ascii_strdown (expected, -1); + want = g_ascii_strdown (wanted, -1); + + /* pad want with 'a's */ + if ((len = strlen (want)) < 4) { + gchar *new_want = g_strndup (want, 4); + while (len < 4) { + new_want[len] = 'a'; + len++; + } + g_free (want); + want = new_want; + } + + /* pad expect with 'a's */ + if ((len = strlen (expect)) < 4) { + gchar *new_expect = g_strndup (expect, 4); + while (len < 4) { + new_expect[len] = 'a'; + len++; + } + g_free (expect); + expect = new_expect; + } + + /* build the swizzle format */ + while (want && want[0] != '\0') { + gchar *val; + gint idx; + gchar needle = want[0]; + + if (needle == 'x') + needle = 'a'; + + if (!(val = strchr (expect, needle)) + && needle == 'a' && !(val = strchr (expect, 'x'))) + goto fail; + + idx = (gint) (val - expect); + + ret = g_string_append_c (ret, _index_to_shader_swizzle (idx)); + want = &want[1]; + } + + return g_string_free (ret, FALSE); + +fail: + g_string_free (ret, TRUE); + return NULL; +} + +static void +_RGB_to_RGB (GstGLContext * context, GstGLColorConvert * convert) +{ + struct ConvertInfo *info = &convert->priv->convert_info; + GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info); + const gchar *in_format_str = gst_video_format_to_string (in_format); + GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info); + const gchar *out_format_str = gst_video_format_to_string (out_format); + gchar *pixel_order = _RGB_pixel_order (in_format_str, out_format_str); + + info->in_n_textures = 1; + info->out_n_textures = 1; + info->frag_prog = g_strdup_printf (frag_REORDER, pixel_order[0], + pixel_order[1], pixel_order[2], pixel_order[3]); + info->shader_tex_names[0] = "tex"; + info->shader_scaling[0][0] = 1.0f; + info->shader_scaling[0][1] = 1.0f; +} + +static void +_YUV_to_RGB (GstGLContext * context, GstGLColorConvert * convert) +{ + struct ConvertInfo *info = &convert->priv->convert_info; + GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info); + const gchar *out_format_str = gst_video_format_to_string (out_format); + gchar *pixel_order = _RGB_pixel_order ("rgba", out_format_str); + + info->out_n_textures = 1; + + info->shader_scaling[0][0] = 1.0f; + info->shader_scaling[0][1] = 1.0f; + info->shader_scaling[1][0] = 1.0f; + info->shader_scaling[1][1] = 1.0f; + info->shader_scaling[2][0] = 1.0f; + info->shader_scaling[2][1] = 1.0f; + + switch (GST_VIDEO_INFO_FORMAT (&convert->in_info)) { + case GST_VIDEO_FORMAT_AYUV: + info->frag_prog = g_strdup_printf (frag_AYUV_to_RGB, pixel_order[0], + pixel_order[1], pixel_order[2], pixel_order[3]); + info->in_n_textures = 1; + info->shader_tex_names[0] = "tex"; + break; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y41B: + info->frag_prog = g_strdup_printf (frag_PLANAR_YUV_to_RGB, pixel_order[0], + pixel_order[1], pixel_order[2], pixel_order[3]); + info->in_n_textures = 3; + info->shader_tex_names[0] = "Ytex"; + info->shader_tex_names[1] = "Utex"; + info->shader_tex_names[2] = "Vtex"; + break; + case GST_VIDEO_FORMAT_YUY2: + info->frag_prog = g_strdup_printf (frag_YUY2_UYVY_to_RGB, 'r', 'g', 'a', + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + info->in_n_textures = 2; + info->shader_tex_names[0] = "Ytex"; + info->shader_tex_names[1] = "UVtex"; + break; + case GST_VIDEO_FORMAT_NV12: + info->frag_prog = g_strdup_printf (frag_NV12_NV21_to_RGB, 'r', 'a', + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + info->in_n_textures = 2; + info->shader_tex_names[0] = "Ytex"; + info->shader_tex_names[1] = "UVtex"; + break; + case GST_VIDEO_FORMAT_NV21: + info->frag_prog = g_strdup_printf (frag_NV12_NV21_to_RGB, 'a', 'r', + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + info->in_n_textures = 2; + info->shader_tex_names[0] = "Ytex"; + info->shader_tex_names[1] = "UVtex"; + break; + case GST_VIDEO_FORMAT_UYVY: + info->frag_prog = g_strdup_printf (frag_YUY2_UYVY_to_RGB, 'a', 'r', 'b', + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + info->in_n_textures = 2; + info->shader_tex_names[0] = "Ytex"; + info->shader_tex_names[1] = "UVtex"; + break; + default: + break; + } + + if (gst_video_colorimetry_matches (&convert->in_info.colorimetry, + GST_VIDEO_COLORIMETRY_BT709)) { + info->cms_offset = (gfloat *) from_yuv_bt709_offset; + info->cms_coeff1 = (gfloat *) from_yuv_bt709_rcoeff; + info->cms_coeff2 = (gfloat *) from_yuv_bt709_gcoeff; + info->cms_coeff3 = (gfloat *) from_yuv_bt709_bcoeff; + } else { + /* defaults/bt601 */ + info->cms_offset = (gfloat *) from_yuv_bt601_offset; + info->cms_coeff1 = (gfloat *) from_yuv_bt601_rcoeff; + info->cms_coeff2 = (gfloat *) from_yuv_bt601_gcoeff; + info->cms_coeff3 = (gfloat *) from_yuv_bt601_bcoeff; + } + + g_free (pixel_order); +} + +static void +_RGB_to_YUV (GstGLContext * context, GstGLColorConvert * convert) +{ + struct ConvertInfo *info = &convert->priv->convert_info; + GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info); + const gchar *in_format_str = gst_video_format_to_string (in_format); + gchar *pixel_order = _RGB_pixel_order (in_format_str, "rgba"); + + info->frag_prog = NULL; + info->in_n_textures = 1; + + info->shader_tex_names[0] = "tex"; + + info->shader_scaling[0][0] = 1.0f; + info->shader_scaling[0][1] = 1.0f; + info->shader_scaling[1][0] = 1.0f; + info->shader_scaling[1][1] = 1.0f; + info->shader_scaling[2][0] = 1.0f; + info->shader_scaling[2][1] = 1.0f; + + switch (GST_VIDEO_INFO_FORMAT (&convert->out_info)) { + case GST_VIDEO_FORMAT_AYUV: + info->frag_prog = g_strdup_printf (frag_RGB_to_AYUV, pixel_order[0], + pixel_order[1], pixel_order[2], pixel_order[3]); + info->out_n_textures = 1; + break; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y41B: + info->frag_prog = g_strdup_printf (frag_RGB_to_PLANAR_YUV, + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3], + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + info->out_n_textures = 3; + break; + case GST_VIDEO_FORMAT_YUY2: + info->frag_prog = g_strdup_printf (frag_RGB_to_YUY2_UYVY, + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3], + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3], + "y1,u,y2,v"); + info->out_n_textures = 1; + break; + case GST_VIDEO_FORMAT_UYVY: + info->frag_prog = g_strdup_printf (frag_RGB_to_YUY2_UYVY, + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3], + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3], + "u,y1,v,y2"); + info->out_n_textures = 1; + break; + default: + break; + } + + if (gst_video_colorimetry_matches (&convert->in_info.colorimetry, + GST_VIDEO_COLORIMETRY_BT709)) { + info->cms_offset = (gfloat *) from_rgb_bt709_offset; + info->cms_coeff1 = (gfloat *) from_rgb_bt709_ycoeff; + info->cms_coeff2 = (gfloat *) from_rgb_bt709_ucoeff; + info->cms_coeff3 = (gfloat *) from_rgb_bt709_vcoeff; + } else { + /* defaults/bt601 */ + info->cms_offset = (gfloat *) from_rgb_bt601_offset; + info->cms_coeff1 = (gfloat *) from_rgb_bt601_ycoeff; + info->cms_coeff2 = (gfloat *) from_rgb_bt601_ucoeff; + info->cms_coeff3 = (gfloat *) from_rgb_bt601_vcoeff; + } + + g_free (pixel_order); +} + +static void +_RGB_to_GRAY (GstGLContext * context, GstGLColorConvert * convert) +{ + struct ConvertInfo *info = &convert->priv->convert_info; + GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info); + const gchar *in_format_str = gst_video_format_to_string (in_format); + gchar *pixel_order = _RGB_pixel_order (in_format_str, "rgba"); + + info->in_n_textures = 1; + info->out_n_textures = 1; + info->shader_tex_names[0] = "tex"; + info->shader_scaling[0][0] = 1.0f; + info->shader_scaling[0][1] = 1.0f; + info->shader_scaling[1][0] = 1.0f; + info->shader_scaling[1][1] = 1.0f; + info->shader_scaling[2][0] = 1.0f; + info->shader_scaling[2][1] = 1.0f; + + switch (GST_VIDEO_INFO_FORMAT (&convert->out_info)) { + case GST_VIDEO_FORMAT_GRAY8: + info->frag_prog = g_strdup_printf (frag_REORDER, pixel_order[0], + pixel_order[0], pixel_order[0], pixel_order[3]); + break; + default: + break; + } +} + +static void +_GRAY_to_RGB (GstGLContext * context, GstGLColorConvert * convert) +{ + struct ConvertInfo *info = &convert->priv->convert_info; + GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info); + const gchar *out_format_str = gst_video_format_to_string (out_format); + gchar *pixel_order = _RGB_pixel_order ("rgba", out_format_str); + + info->in_n_textures = 1; + info->out_n_textures = 1; + info->shader_tex_names[0] = "tex"; + info->shader_scaling[0][0] = 1.0f; + info->shader_scaling[0][1] = 1.0f; + info->shader_scaling[1][0] = 1.0f; + info->shader_scaling[1][1] = 1.0f; + info->shader_scaling[2][0] = 1.0f; + info->shader_scaling[2][1] = 1.0f; + + switch (GST_VIDEO_INFO_FORMAT (&convert->in_info)) { + case GST_VIDEO_FORMAT_GRAY8: + info->frag_prog = g_strdup_printf (frag_REORDER, pixel_order[0], + pixel_order[1], pixel_order[2], pixel_order[3]); + break; + case GST_VIDEO_FORMAT_GRAY16_LE: + info->frag_prog = g_strdup_printf (frag_COMPOSE, 'a', 'r', + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + break; + case GST_VIDEO_FORMAT_GRAY16_BE: + info->frag_prog = g_strdup_printf (frag_COMPOSE, 'r', 'a', + pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]); + break; + default: + break; + } +} + +/* Called in the gl thread */ +void +_init_convert (GstGLContext * context, GstGLColorConvert * convert) +{ + GstGLFuncs *gl; + gboolean res; + struct ConvertInfo *info = &convert->priv->convert_info; + gint i; + + gl = context->gl_vtable; + + GST_INFO ("Initializing color conversion from %s to %s", + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)), + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->out_info))); + + if (!gl->CreateProgramObject && !gl->CreateProgram) { + gst_gl_context_set_error (context, + "Cannot perform color conversion without OpenGL shaders"); + goto error; + } + + if (GST_VIDEO_INFO_IS_RGB (&convert->in_info)) { + if (GST_VIDEO_INFO_IS_RGB (&convert->out_info)) { + _RGB_to_RGB (context, convert); + } + } + + if (GST_VIDEO_INFO_IS_YUV (&convert->in_info)) { + if (GST_VIDEO_INFO_IS_RGB (&convert->out_info)) { + _YUV_to_RGB (context, convert); + } + } + + if (GST_VIDEO_INFO_IS_RGB (&convert->in_info)) { + if (GST_VIDEO_INFO_IS_YUV (&convert->out_info)) { + _RGB_to_YUV (context, convert); + } + } + + if (GST_VIDEO_INFO_IS_RGB (&convert->in_info)) { + if (GST_VIDEO_INFO_IS_GRAY (&convert->out_info)) { + _RGB_to_GRAY (context, convert); + } + } + + if (GST_VIDEO_INFO_IS_GRAY (&convert->in_info)) { + if (GST_VIDEO_INFO_IS_RGB (&convert->out_info)) { + _GRAY_to_RGB (context, convert); + } + } + + if (!info->frag_prog || info->in_n_textures == 0 || info->out_n_textures == 0) + goto unhandled_format; + + /* multiple draw targets not supported on GLES2...yet */ + if (info->out_n_textures > 1 && (!gl->DrawBuffers || USING_GLES2 (context))) { + g_free (info->frag_prog); + GST_ERROR ("Conversion requires output to multiple draw buffers"); + goto incompatible_api; + } + + res = + _create_shader (context, text_vertex_shader, info->frag_prog, + &convert->shader); + g_free (info->frag_prog); + if (!res) + goto error; + + convert->shader_attr_position_loc = + gst_gl_shader_get_attribute_location (convert->shader, "a_position"); + convert->shader_attr_texture_loc = + gst_gl_shader_get_attribute_location (convert->shader, "a_texcoord"); + + gst_gl_shader_use (convert->shader); + + if (info->cms_offset && info->cms_coeff1 + && info->cms_coeff2 && info->cms_coeff3) { + gst_gl_shader_set_uniform_3fv (convert->shader, "offset", 1, + info->cms_offset); + gst_gl_shader_set_uniform_3fv (convert->shader, "coeff1", 1, + info->cms_coeff1); + gst_gl_shader_set_uniform_3fv (convert->shader, "coeff2", 1, + info->cms_coeff2); + gst_gl_shader_set_uniform_3fv (convert->shader, "coeff3", 1, + info->cms_coeff3); + } + + for (i = info->in_n_textures - 1; i >= 0; i--) { + gchar *scale_name = g_strdup_printf ("tex_scale%u", i); + + gst_gl_shader_set_uniform_1i (convert->shader, info->shader_tex_names[i], + i); + gst_gl_shader_set_uniform_2fv (convert->shader, scale_name, 1, + info->shader_scaling[i]); + + g_free (scale_name); + } + + gst_gl_shader_set_uniform_1f (convert->shader, "width", + GST_VIDEO_INFO_WIDTH (&convert->in_info)); + + gst_gl_context_clear_shader (context); + + if (!_init_convert_fbo (context, convert)) { + goto error; + } + + gl->BindTexture (GL_TEXTURE_2D, 0); + + convert->priv->result = TRUE; + return; + +unhandled_format: + gst_gl_context_set_error (context, "Don't know how to convert from %s to %s", + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)), + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->out_info))); + +error: + convert->priv->result = FALSE; + return; + +incompatible_api: + { + gst_gl_context_set_error (context, "Converting from %s to %s requires " + "functionality that the current OpenGL setup does not support", + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)), + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT + (&convert->out_info))); + convert->priv->result = FALSE; + return; + } +} + + +/* called by _init_convert (in the gl thread) */ +gboolean +_init_convert_fbo (GstGLContext * context, GstGLColorConvert * convert) +{ + GstGLFuncs *gl; + guint out_width, out_height; + GLuint fake_texture = 0; /* a FBO must hava texture to init */ + + gl = context->gl_vtable; + + out_width = GST_VIDEO_INFO_WIDTH (&convert->out_info); + out_height = GST_VIDEO_INFO_HEIGHT (&convert->out_info); + + if (!gl->GenFramebuffers) { + /* turn off the pipeline because Frame buffer object is a not present */ + gst_gl_context_set_error (context, + "Context, EXT_framebuffer_object supported: no"); + return FALSE; + } + + GST_INFO ("Context, EXT_framebuffer_object supported: yes"); + + /* setup FBO */ + gl->GenFramebuffers (1, &convert->fbo); + gl->BindFramebuffer (GL_FRAMEBUFFER, convert->fbo); + + /* setup the render buffer for depth */ + gl->GenRenderbuffers (1, &convert->depth_buffer); + gl->BindRenderbuffer (GL_RENDERBUFFER, convert->depth_buffer); + if (USING_OPENGL (context)) { + gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, + out_width, out_height); + gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, + out_width, out_height); + } + if (USING_GLES2 (context)) { + gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, + out_width, out_height); + } + + /* a fake texture is attached to the convert FBO (cannot init without it) */ + gl->GenTextures (1, &fake_texture); + gl->BindTexture (GL_TEXTURE_2D, fake_texture); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, out_width, out_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + /* attach the texture to the FBO to renderer to */ + gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, fake_texture, 0); + + /* attach the depth render buffer to the FBO */ + gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, convert->depth_buffer); + + if (USING_OPENGL (context)) { + gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, convert->depth_buffer); + } + + if (!gst_gl_context_check_framebuffer_status (context)) { + gst_gl_context_set_error (context, "GL framebuffer status incomplete"); + return FALSE; + } + + /* unbind the FBO */ + gl->BindFramebuffer (GL_FRAMEBUFFER, 0); + + gl->DeleteTextures (1, &fake_texture); + + return TRUE; +} + +/* Called by the idle function in the gl thread */ +void +_do_convert (GstGLContext * context, GstGLColorConvert * convert) +{ + guint in_width, in_height, out_width, out_height; + + out_width = GST_VIDEO_INFO_WIDTH (&convert->out_info); + out_height = GST_VIDEO_INFO_HEIGHT (&convert->out_info); + in_width = GST_VIDEO_INFO_WIDTH (&convert->in_info); + in_height = GST_VIDEO_INFO_HEIGHT (&convert->in_info); + + GST_TRACE ("converting to textures:%u,%u,%u,%u dimensions:%ux%u, " + "from textures:%u,%u,%u,%u dimensions:%ux%u", convert->out_tex[0], + convert->out_tex[1], convert->out_tex[2], convert->out_tex[3], + out_width, out_height, convert->in_tex[0], convert->in_tex[1], + convert->in_tex[2], convert->in_tex[3], in_width, in_height); + + if (!convert->priv->draw (context, convert)) + goto error; + + convert->priv->result = TRUE; + return; + +error: + { + convert->priv->result = FALSE; + return; + } +} + +static gboolean +_do_convert_draw (GstGLContext * context, GstGLColorConvert * convert) +{ + GstGLFuncs *gl; + struct ConvertInfo *c_info = &convert->priv->convert_info; + guint out_width, out_height; + gint i; + + const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f, + 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + 0.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f + }; + + GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; + + GLenum multipleRT[] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2 + }; + + gl = context->gl_vtable; + + out_width = GST_VIDEO_INFO_WIDTH (&convert->out_info); + out_height = GST_VIDEO_INFO_HEIGHT (&convert->out_info); + + gl->BindFramebuffer (GL_FRAMEBUFFER, convert->fbo); + + /* attach the texture to the FBO to renderer to */ + for (i = 0; i < c_info->out_n_textures; i++) { + /* needed? */ + gl->BindTexture (GL_TEXTURE_2D, convert->out_tex[i]); + + gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, + GL_TEXTURE_2D, convert->out_tex[i], 0); + } + + if (gl->DrawBuffers) + gl->DrawBuffers (c_info->out_n_textures, multipleRT); + else if (gl->DrawBuffer) + gl->DrawBuffer (GL_COLOR_ATTACHMENT0); + + gl->Viewport (0, 0, out_width, out_height); + + gl->ClearColor (0.0, 0.0, 0.0, 0.0); + gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gst_gl_shader_use (convert->shader); + + gl->VertexAttribPointer (convert->shader_attr_position_loc, 3, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); + gl->VertexAttribPointer (convert->shader_attr_texture_loc, 2, + GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); + + gl->EnableVertexAttribArray (convert->shader_attr_position_loc); + gl->EnableVertexAttribArray (convert->shader_attr_texture_loc); + + for (i = c_info->in_n_textures - 1; i >= 0; i--) { + gl->ActiveTexture (GL_TEXTURE0 + i); + gl->BindTexture (GL_TEXTURE_2D, convert->in_tex[i]); + + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + + gl->DisableVertexAttribArray (convert->shader_attr_position_loc); + gl->DisableVertexAttribArray (convert->shader_attr_texture_loc); + + if (gl->DrawBuffer) + gl->DrawBuffer (GL_NONE); + + /* we are done with the shader */ + gst_gl_context_clear_shader (context); + + gst_gl_context_check_framebuffer_status (context); + + gl->BindFramebuffer (GL_FRAMEBUFFER, 0); + + return TRUE; +} diff --git a/gst-libs/gst/gl/gstglcolorconvert.h b/gst-libs/gst/gl/gstglcolorconvert.h new file mode 100644 index 0000000000..48322688a7 --- /dev/null +++ b/gst-libs/gst/gl/gstglcolorconvert.h @@ -0,0 +1,117 @@ +/* + * GStreamer + * Copyright (C) 2012 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_COLOR_CONVERT_H__ +#define __GST_GL_COLOR_CONVERT_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +GType gst_gl_color_convert_get_type (void); +#define GST_TYPE_GL_COLOR_CONVERT (gst_gl_color_convert_get_type()) +#define GST_GL_COLOR_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_COLOR_CONVERT,GstGLColorConvert)) +#define GST_GL_COLOR_CONVERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GL_DISPLAY,GstGLColorConvertClass)) +#define GST_IS_GL_COLOR_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_COLOR_CONVERT)) +#define GST_IS_GL_COLOR_CONVERT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GL_COLOR_CONVERT)) +#define GST_GL_COLOR_CONVERT_CAST(obj) ((GstGLColorConvert*)(obj)) + +/** + * GstGLColorConvert + * + * Opaque #GstGLColorConvert object + */ +struct _GstGLColorConvert +{ + /* */ + GObject parent; + + GMutex lock; + + GstGLContext *context; + + /* input data */ + GstVideoInfo in_info; + GstVideoInfo out_info; + + gboolean initted; + + guint in_tex[GST_VIDEO_MAX_PLANES]; + guint out_tex[GST_VIDEO_MAX_PLANES]; + + /* used for the conversion */ + GLuint fbo; + GLuint depth_buffer; + GstGLShader *shader; + GLint shader_attr_position_loc; + GLint shader_attr_texture_loc; + + /* */ + GstGLColorConvertPrivate *priv; + + gpointer _reserved[GST_PADDING]; +}; + +/** + * GstGLColorConvertClass: + * + * The #GstGLColorConvertClass struct only contains private data + */ +struct _GstGLColorConvertClass +{ + GObjectClass object_class; +}; + +/** + * GST_GL_COLOR_CONVERT_FORMATS: + * + * The currently supported formats that can be converted + */ +#define GST_GL_COLOR_CONVERT_FORMATS "{ RGB, RGBx, RGBA, BGR, BGRx, BGRA, xRGB, " \ + "xBGR, ARGB, ABGR, Y444, I420, YV12, Y42B, " \ + "Y41B, NV12, NV21, YUY2, UYVY, AYUV, " \ + "GRAY8, GRAY16_LE, GRAY16_BE }" + +/** + * GST_GL_COLOR_CONVERT_VIDEO_CAPS: + * + * The currently supported #GstCaps that can be converted + */ +#define GST_GL_COLOR_CONVERT_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS) + +GstGLColorConvert * gst_gl_color_convert_new (GstGLContext * context); + +gboolean gst_gl_color_convert_init_format (GstGLColorConvert * convert, + GstVideoInfo in_info, + GstVideoInfo out_info); + +void gst_gl_color_convert_set_texture_scaling (GstGLColorConvert * convert, + gfloat scaling[GST_VIDEO_MAX_PLANES][2]); + +gboolean gst_gl_color_convert_perform (GstGLColorConvert * convert, + guint in_tex[GST_VIDEO_MAX_PLANES], + guint out_tex[GST_VIDEO_MAX_PLANES]); + +G_END_DECLS + +#endif /* __GST_GL_COLOR_CONVERT_H__ */ diff --git a/gst-libs/gst/gl/gstgldownload.c b/gst-libs/gst/gl/gstgldownload.c index 425747df5e..a20bc41a22 100644 --- a/gst-libs/gst/gl/gstgldownload.c +++ b/gst-libs/gst/gl/gstgldownload.c @@ -45,200 +45,9 @@ static void _do_download (GstGLContext * context, GstGLDownload * download); static void _init_download (GstGLContext * context, GstGLDownload * download); -static gboolean _init_download_shader (GstGLContext * context, - GstGLDownload * download); static gboolean _gst_gl_download_perform_with_data_unlocked (GstGLDownload * download, GLuint texture_id, gpointer data[GST_VIDEO_MAX_PLANES]); -#if GST_GL_HAVE_OPENGL -static void _do_download_draw_rgb_opengl (GstGLContext * context, - GstGLDownload * download); -static void _do_download_draw_yuv_opengl (GstGLContext * context, - GstGLDownload * download); -#endif -#if GST_GL_HAVE_GLES2 -static void _do_download_draw_rgb_gles2 (GstGLContext * context, - GstGLDownload * download); -static void _do_download_draw_yuv_gles2 (GstGLContext * context, - GstGLDownload * download); -#endif - -/* *INDENT-OFF* */ - -/* FIXME: use the colormatrix support from videoconvert */ - -#define RGB_TO_YUV_COEFFICIENTS \ - "uniform vec3 offset;\n" \ - "uniform vec3 ycoeff;\n" \ - "uniform vec3 ucoeff;\n" \ - "uniform vec3 vcoeff;\n" - -/* Matrix inverses of the color matrices found in gstglupload */ -/* BT. 601 standard with the following ranges: - * Y = [16..235] (of 255) - * Cb/Cr = [16..240] (of 255) - */ -static const gfloat bt601_offset[] = {0.0625, 0.5, 0.5}; -static const gfloat bt601_ycoeff[] = {0.256816, 0.504154, 0.0979137}; -static const gfloat bt601_ucoeff[] = {-0.148246, -0.29102, 0.439266}; -static const gfloat bt601_vcoeff[] = {0.439271, -0.367833, -0.071438}; - -/* BT. 709 standard with the following ranges: - * Y = [16..235] (of 255) - * Cb/Cr = [16..240] (of 255) - */ -static const gfloat bt709_offset[] = {0.0625, 0.5, 0.5}; -static const gfloat bt709_ycoeff[] = {0.213392, 0.718140,-0.072426}; -static const gfloat bt709_ucoeff[] = {0.117608, 0.395793,-0.513401}; -static const gfloat bt709_vcoeff[] = {0.420599,-0.467775, 0.047176}; - -#if GST_GL_HAVE_OPENGL -/* YUY2:y2,u,y1,v - UYVY:v,y1,u,y2 */ -static const gchar *text_shader_YUY2_UYVY_opengl = - "uniform sampler2D tex;\n" - "uniform float width;\n" - RGB_TO_YUV_COEFFICIENTS - "void main(void) {\n" - " vec3 rgb1, rgb2;\n" - " float fx,fy,y1,y2,u,v;\n" - " fx = gl_TexCoord[0].x;\n" - " fy = gl_TexCoord[0].y;\n" - " rgb1=texture2D(tex,vec2(fx*2.0,fy)).rgb;\n" - " rgb2=texture2D(tex,vec2(fx*2.0+1.0/width,fy)).rgb;\n" - " y1=dot(rgb1, ycoeff);\n" - " y2=dot(rgb2, ycoeff);\n" - " u=dot(rgb1, ucoeff);\n" - " v=dot(rgb1, vcoeff);\n" - " y1+=offset.x;\n" - " y2+=offset.x;\n" - " u+=offset.y;\n" - " v+=offset.z;\n" - " gl_FragColor=vec4(%s);\n" - "}\n"; - -static const gchar *text_shader_I420_YV12_opengl = - "uniform sampler2D tex;\n" - "uniform float w, h;\n" - RGB_TO_YUV_COEFFICIENTS - "void main(void) {\n" - " vec3 rgb1, rgb2;\n" - " float y,u,v;\n" - " vec2 nxy=gl_TexCoord[0].xy;\n" - " vec2 nxy2=nxy*2.0;\n" - " rgb1=texture2D(tex,nxy).rgb;\n" - " rgb2=texture2D(tex,nxy2).rgb;\n" - " y=dot(rgb1, ycoeff);\n" - " u=dot(rgb2, ucoeff);\n" - " v=dot(rgb2, vcoeff);\n" - " y+=offset.x;\n" - " u+=offset.y;\n" - " v+=offset.z;\n" - " gl_FragData[0] = vec4(y, 0.0, 0.0, 1.0);\n" - " gl_FragData[1] = vec4(u, 0.0, 0.0, 1.0);\n" - " gl_FragData[2] = vec4(v, 0.0, 0.0, 1.0);\n" - "}\n"; - -static const gchar *text_shader_AYUV_opengl = - "uniform sampler2D tex;\n" - RGB_TO_YUV_COEFFICIENTS - "void main(void) {\n" - " vec3 rgb;\n" - " float y,u,v;\n" - " vec2 nxy=gl_TexCoord[0].xy;\n" - " rgb=texture2D(tex,nxy).rgb;\n" - " y=dot(rgb, ycoeff);\n" - " u=dot(rgb, ucoeff);\n" - " v=dot(rgb, vcoeff);\n" - " y+=offset.x;\n" - " u+=offset.y;\n" - " v+=offset.z;\n" - " gl_FragColor=vec4(y,u,v,1.0);\n" - "}\n"; - -#define text_vertex_shader_opengl NULL -#endif /* GST_GL_HAVE_OPENGL */ - -#if GST_GL_HAVE_GLES2 -static const gchar *text_shader_YUY2_UYVY_gles2 = - "precision mediump float;\n" - "varying vec2 v_texCoord;\n" - "uniform sampler2D tex;\n" - RGB_TO_YUV_COEFFICIENTS - "void main(void) {\n" - " vec3 rgb1, rgb2;\n" - " float fx,fy,y1,y2,u,v;\n" - " fx = v_texCoord.x;\n" - " fy = v_texCoord.y;\n" - " rgb1=texture2D(tex,vec2(fx*2.0,fy)).rgb;\n" - " rgb2=texture2D(tex,vec2(fx*2.0+1.0,fy)).rgb;\n" - " y1=dot(rgb1, ycoeff);\n" - " y2=dot(rgb2, ycoeff);\n" - " u=dot(rgb1, ucoeff);\n" - " v=dot(rgb1, vcoeff);\n" - " y1+=offset.x;\n" - " y2+=offset.x;\n" - " u+=offset.y;\n" - " v+=offset.z;\n" - " gl_FragColor=vec4(%s);\n" - "}\n"; - -/* no OpenGL ES 2.0 support because for now it's not possible - * to attach multiple textures to a frame buffer object - */ -#define text_shader_I420_YV12_gles2 NULL - -static const gchar *text_shader_AYUV_gles2 = - "precision mediump float;\n" - "varying vec2 v_texCoord;\n" - "uniform sampler2D tex;\n" - RGB_TO_YUV_COEFFICIENTS - "void main(void) {\n" - " vec3 rgb;\n" - " float y,u,v;\n" - " vec2 nxy=v_texCoord.xy;\n" - " rgb=texture2D(tex,nxy).rgb;\n" - " y=dot(rgb, ycoeff);\n" - " u=dot(rgb, ucoeff);\n" - " v=dot(rgb, vcoeff);\n" - " y+=offset.x;\n" - " u+=offset.y;\n" - " v+=offset.z;\n" - " gl_FragColor=vec4(1.0,y,u,v);\n" - "}\n"; - -static const gchar *text_shader_ARGB_gles2 = - "precision mediump float;\n" - "varying vec2 v_texCoord;\n" - "uniform sampler2D tex;\n" - "void main(void) {\n" - " vec4 rgba;\n" - " vec2 nxy = v_texCoord.xy;\n" - " rgba.rgb=texture2D(tex,nxy).rgb;\n" - " rgba.a = 1.0;\n" - " gl_FragColor=vec4(rgba.%c,rgba.%c,rgba.%c,rgba.%c);\n" - "}\n"; - -static const gchar *text_vertex_shader_gles2 = - "attribute vec4 a_position; \n" - "attribute vec2 a_texCoord; \n" - "varying vec2 v_texCoord; \n" - "void main() \n" - "{ \n" - " gl_Position = a_position; \n" - " v_texCoord = a_texCoord; \n" - "} \n"; - -static const gchar *text_shader_RGB_gles2 = - "precision mediump float; \n" - "varying vec2 v_texCoord; \n" - "uniform sampler2D tex; \n" - "void main() \n" - "{ \n" - " gl_FragColor = texture2D(tex, v_texCoord ); \n" - "} \n"; -#endif /* GST_GL_HAVE_GLES2 */ - /* *INDENT-ON* */ struct _GstGLDownloadPrivate @@ -249,9 +58,6 @@ struct _GstGLDownloadPrivate const gchar *ARGB; const gchar *vert_shader; - void (*do_rgb) (GstGLContext * context, GstGLDownload * download); - void (*do_yuv) (GstGLContext * context, GstGLDownload * download); - gboolean result; }; @@ -282,18 +88,8 @@ gst_gl_download_init (GstGLDownload * download) download->priv = GST_GL_DOWNLOAD_GET_PRIVATE (download); - download->context = NULL; - g_mutex_init (&download->lock); - download->fbo = 0; - download->depth_buffer = 0; - download->in_texture = 0; - download->shader = NULL; - - download->shader_attr_position_loc = 0; - download->shader_attr_texture_loc = 0; - gst_video_info_init (&download->info); } @@ -307,35 +103,11 @@ GstGLDownload * gst_gl_download_new (GstGLContext * context) { GstGLDownload *download; - GstGLDownloadPrivate *priv; download = g_object_new (GST_TYPE_GL_DOWNLOAD, NULL); download->context = gst_object_ref (context); - priv = download->priv; - -#if GST_GL_HAVE_OPENGL - if (USING_OPENGL (context)) { - priv->YUY2_UYVY = text_shader_YUY2_UYVY_opengl; - priv->I420_YV12 = text_shader_I420_YV12_opengl; - priv->AYUV = text_shader_AYUV_opengl; - priv->ARGB = NULL; - priv->vert_shader = text_vertex_shader_opengl; - priv->do_rgb = _do_download_draw_rgb_opengl; - priv->do_yuv = _do_download_draw_yuv_opengl; - } -#endif -#if GST_GL_HAVE_GLES2 - if (USING_GLES2 (context)) { - priv->YUY2_UYVY = text_shader_YUY2_UYVY_gles2; - priv->I420_YV12 = text_shader_I420_YV12_gles2; - priv->AYUV = text_shader_AYUV_gles2; - priv->ARGB = text_shader_ARGB_gles2; - priv->vert_shader = text_vertex_shader_gles2; - priv->do_rgb = _do_download_draw_rgb_gles2; - priv->do_yuv = _do_download_draw_yuv_gles2; - } -#endif + download->convert = gst_gl_color_convert_new (context); return download; } @@ -359,15 +131,9 @@ gst_gl_download_finalize (GObject * object) gst_gl_context_del_texture (download->context, &download->in_texture); download->in_texture = 0; } - if (download->fbo || download->depth_buffer) { - gst_gl_context_del_fbo (download->context, download->fbo, - download->depth_buffer); - download->fbo = 0; - download->depth_buffer = 0; - } - if (download->shader) { - gst_object_unref (download->shader); - download->shader = NULL; + if (download->convert) { + gst_object_unref (download->convert); + download->convert = NULL; } if (download->context) { @@ -527,9 +293,10 @@ _gst_gl_download_perform_with_data_unlocked (GstGLDownload * download, static void _init_download (GstGLContext * context, GstGLDownload * download) { - GstGLFuncs *gl; + const GstGLFuncs *gl; GstVideoFormat v_format; guint out_width, out_height; + GstVideoInfo in_info; gl = context->gl_vtable; v_format = GST_VIDEO_INFO_FORMAT (&download->info); @@ -539,523 +306,125 @@ _init_download (GstGLContext * context, GstGLDownload * download) GST_TRACE ("initializing texture download for format %s", gst_video_format_to_string (v_format)); - if (USING_OPENGL (context)) { - switch (v_format) { - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - goto no_convert; - break; - default: - break; - } - } + gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_RGBA, out_width, + out_height); - switch (v_format) { - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - case GST_VIDEO_FORMAT_AYUV: - /* color space conversion is needed */ - { - - if (!gl->GenFramebuffers) { - /* Frame buffer object is a requirement - * when using GLSL colorspace conversion - */ - gst_gl_context_set_error (context, - "Context, EXT_framebuffer_object supported: no"); - goto error; - } - GST_INFO ("Context, EXT_framebuffer_object supported: yes"); - - /* setup FBO */ - gl->GenFramebuffers (1, &download->fbo); - gl->BindFramebuffer (GL_FRAMEBUFFER, download->fbo); - - /* setup the render buffer for depth */ - gl->GenRenderbuffers (1, &download->depth_buffer); - gl->BindRenderbuffer (GL_RENDERBUFFER, download->depth_buffer); -#if GST_GL_HAVE_OPENGL - if (USING_OPENGL (context)) { - gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, - out_width, out_height); - gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, - out_width, out_height); - } -#endif -#if GST_GL_HAVE_GLES2 - if (USING_GLES2 (context)) { - gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, - out_width, out_height); - } -#endif - - /* setup a first texture to render to */ - gl->GenTextures (1, &download->out_texture[0]); - gl->BindTexture (GL_TEXTURE_2D, download->out_texture[0]); - gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, - out_width, out_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - /* attach the first texture to the FBO to renderer to */ - gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, download->out_texture[0], 0); - - if (v_format == GST_VIDEO_FORMAT_I420 || - v_format == GST_VIDEO_FORMAT_YV12) { - /* setup a second texture to render to */ - gl->GenTextures (1, &download->out_texture[1]); - gl->BindTexture (GL_TEXTURE_2D, download->out_texture[1]); - gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, - out_width, out_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - /* attach the second texture to the FBO to renderer to */ - gl->FramebufferTexture2D (GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, download->out_texture[1], 0); - - /* setup a third texture to render to */ - gl->GenTextures (1, &download->out_texture[2]); - gl->BindTexture (GL_TEXTURE_2D, download->out_texture[2]); - gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, - out_width, out_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - /* attach the third texture to the FBO to renderer to */ - gl->FramebufferTexture2D (GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, download->out_texture[2], 0); - } - - /* attach the depth render buffer to the FBO */ - gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, download->depth_buffer); - if (USING_OPENGL (context)) { - gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, download->depth_buffer); - } - - if (!gst_gl_context_check_framebuffer_status (context)) { - gst_gl_context_set_error (context, "GL framebuffer status incomplete"); - goto error; - } - - /* unbind the FBO */ - gl->BindFramebuffer (GL_FRAMEBUFFER, 0); - } - break; - default: - break; - gst_gl_context_set_error (context, "Unsupported download video format %d", - v_format); - g_assert_not_reached (); - } - -no_convert: - download->priv->result = _init_download_shader (context, download); - return; - -error: - { - download->priv->result = FALSE; + download->priv->result = + gst_gl_color_convert_init_format (download->convert, in_info, + download->info); + if (!download->priv->result) return; - } -} -static gboolean -_create_shader (GstGLContext * context, const gchar * vertex_src, - const gchar * fragment_src, GstGLShader ** out_shader) -{ - GstGLShader *shader; - GError *error = NULL; - - g_return_val_if_fail (vertex_src != NULL || fragment_src != NULL, FALSE); - - shader = gst_gl_shader_new (context); - - if (vertex_src) - gst_gl_shader_set_vertex_source (shader, vertex_src); - if (fragment_src) - gst_gl_shader_set_fragment_source (shader, fragment_src); - - if (!gst_gl_shader_compile (shader, &error)) { - gst_gl_context_set_error (context, "%s", error->message); - g_error_free (error); - gst_gl_context_clear_shader (context); - gst_object_unref (shader); - return FALSE; - } - - *out_shader = shader; - return TRUE; -} - -static gboolean -_init_download_shader (GstGLContext * context, GstGLDownload * download) -{ - GstGLFuncs *gl; - GstVideoFormat v_format; - - gl = download->context->gl_vtable; - v_format = GST_VIDEO_INFO_FORMAT (&download->info); - - if (GST_VIDEO_FORMAT_INFO_IS_RGB (download->info.finfo) - && !USING_GLES2 (context)) { - switch (v_format) { - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - return TRUE; - break; - default: - break; + if (USING_GLES2 (context) && !USING_GLES3 (context)) { + /* GL_RGBA is the only officially supported texture format in GLES2 */ + if (v_format == GST_VIDEO_FORMAT_RGB || v_format == GST_VIDEO_FORMAT_BGR) { + download->priv->result = FALSE; + return; } } - /* color space conversion is needed */ + gl->GenTextures (1, &download->out_texture[0]); + gl->BindTexture (GL_TEXTURE_2D, download->out_texture[0]); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + out_width, out_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - /* check if fragment shader is available, then load them - * GLSL is a requirement for download - */ - if (!gl->CreateProgramObject && !gl->CreateProgram) { - /* colorspace conversion is not possible */ - gst_gl_context_set_error (context, - "Context, ARB_fragment_shader supported: no"); - return FALSE;; - } + if (v_format == GST_VIDEO_FORMAT_I420 || v_format == GST_VIDEO_FORMAT_YV12) { + /* setup a second texture to render to */ + gl->GenTextures (1, &download->out_texture[1]); + gl->BindTexture (GL_TEXTURE_2D, download->out_texture[1]); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + out_width, out_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - switch (v_format) { - case GST_VIDEO_FORMAT_YUY2: - { - gchar text_shader_download_YUY2[2048]; - - sprintf (text_shader_download_YUY2, - download->priv->YUY2_UYVY, "y2,u,y1,v"); - - if (_create_shader (context, download->priv->vert_shader, - text_shader_download_YUY2, &download->shader)) { - if (USING_GLES2 (context)) { - download->shader_attr_position_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_position"); - download->shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_texCoord"); - } - } - } - break; - case GST_VIDEO_FORMAT_UYVY: - { - gchar text_shader_download_UYVY[2048]; - - sprintf (text_shader_download_UYVY, - download->priv->YUY2_UYVY, "v,y1,u,y2"); - - if (_create_shader (context, download->priv->vert_shader, - text_shader_download_UYVY, &download->shader)) { - if (USING_GLES2 (context)) { - download->shader_attr_position_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_position"); - download->shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_texCoord"); - } - } - } - break; - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - { - _create_shader (context, download->priv->vert_shader, - download->priv->I420_YV12, &download->shader); - break; - } - case GST_VIDEO_FORMAT_AYUV: - { - if (_create_shader (context, download->priv->vert_shader, - download->priv->AYUV, &download->shader)) { - if (USING_GLES2 (context)) { - download->shader_attr_position_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_position"); - download->shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_texCoord"); - } - } - break; - } - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: -#if GST_GL_HAVE_GLES2 - { - gchar text_shader_ARGB[2048]; - - switch (v_format) { - case GST_VIDEO_FORMAT_BGR: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_BGRA: - sprintf (text_shader_ARGB, download->priv->ARGB, 'b', 'g', 'r', 'a'); - break; - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_ARGB: - sprintf (text_shader_ARGB, download->priv->ARGB, 'a', 'r', 'g', 'b'); - break; - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_ABGR: - sprintf (text_shader_ARGB, download->priv->ARGB, 'a', 'b', 'g', 'r'); - break; - default: - memcpy (text_shader_ARGB, text_shader_RGB_gles2, - strlen (text_shader_RGB_gles2) + 1); - break; - } - - if (_create_shader (context, download->priv->vert_shader, - text_shader_ARGB, &download->shader)) { - download->shader_attr_position_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_position"); - download->shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (download->shader, - "a_texCoord"); - } - break; - } -#else - g_assert_not_reached (); - return FALSE; - break; -#endif - default: - gst_gl_context_set_error (context, - "Unsupported download video format %d", v_format); - g_assert_not_reached (); - return FALSE; - break; - } - - return TRUE; -} - -/* Called in the gl thread */ -static void -_do_download (GstGLContext * context, GstGLDownload * download) -{ - GstVideoFormat v_format; - guint out_width, out_height; - - v_format = GST_VIDEO_INFO_FORMAT (&download->info); - out_width = GST_VIDEO_INFO_WIDTH (&download->info); - out_height = GST_VIDEO_INFO_HEIGHT (&download->info); - - GST_TRACE ("downloading texture:%u format:%d, dimensions:%ux%u", - download->in_texture, v_format, out_width, out_height); - - switch (v_format) { - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - /* color space conversion is not needed */ - download->priv->do_rgb (context, download); - break; - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - case GST_VIDEO_FORMAT_AYUV: - /* color space conversion is needed */ - download->priv->do_yuv (context, download); - break; - default: - gst_gl_context_set_error (context, "Unsupported download video format %d", - v_format); - g_assert_not_reached (); - break; + /* setup a third texture to render to */ + gl->GenTextures (1, &download->out_texture[2]); + gl->BindTexture (GL_TEXTURE_2D, download->out_texture[2]); + gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, + out_width, out_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } download->priv->result = TRUE; } -#if GST_GL_HAVE_OPENGL static void -_do_download_draw_rgb_opengl (GstGLContext * context, GstGLDownload * download) -{ - GstGLFuncs *gl; - GstVideoFormat v_format; - - gl = download->context->gl_vtable; - - gst_gl_context_clear_shader (context); - - gl->Enable (GL_TEXTURE_2D); - gl->BindTexture (GL_TEXTURE_2D, download->in_texture); - - v_format = GST_VIDEO_INFO_FORMAT (&download->info); - - switch (v_format) { - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_RGBx: - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, - GL_UNSIGNED_BYTE, download->data[0]); - break; - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_ARGB: -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8, download->data[0]); -#else - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, download->data[0]); -#endif /* G_BYTE_ORDER */ - break; - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_BGRA: - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, - GL_UNSIGNED_BYTE, download->data[0]); - break; - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_ABGR: -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, - GL_UNSIGNED_INT_8_8_8_8, download->data[0]); -#else - glGetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, - GL_UNSIGNED_INT_8_8_8_8_REV, download->data[0]); -#endif /* G_BYTE_ORDER */ - break; - case GST_VIDEO_FORMAT_RGB: - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_RGB, - GL_UNSIGNED_BYTE, download->data[0]); - break; - case GST_VIDEO_FORMAT_BGR: - gl->GetTexImage (GL_TEXTURE_2D, 0, GL_BGR, - GL_UNSIGNED_BYTE, download->data[0]); - break; - default: - gst_gl_context_set_error (context, - "Download video format inconsistency %d", v_format); - g_assert_not_reached (); - break; - } - - gl->Disable (GL_TEXTURE_2D); -} -#endif - - -#if GST_GL_HAVE_GLES2 -static void -_do_download_draw_rgb_gles2 (GstGLContext * context, GstGLDownload * download) +_do_download (GstGLContext * context, GstGLDownload * download) { GstGLFuncs *gl; GstVideoFormat v_format; guint out_width, out_height; + guint in_tex[] = { download->in_texture, 0, 0, 0 }; - GLint viewport_dim[4]; - - const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f, - 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 0.0f, 0.0f, - -1.0f, 1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f - }; - - GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; - - gl = download->context->gl_vtable; + gl = context->gl_vtable; out_width = GST_VIDEO_INFO_WIDTH (&download->info); out_height = GST_VIDEO_INFO_HEIGHT (&download->info); - - gst_gl_context_check_framebuffer_status (context); - gl->BindFramebuffer (GL_FRAMEBUFFER, download->fbo); - - gl->GetIntegerv (GL_VIEWPORT, viewport_dim); - - gl->Viewport (0, 0, out_width, out_height); - - gl->ClearColor (0.0, 0.0, 0.0, 0.0); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - gst_gl_shader_use (download->shader); - - gl->VertexAttribPointer (download->shader_attr_position_loc, 3, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - gl->VertexAttribPointer (download->shader_attr_texture_loc, 2, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - - gl->EnableVertexAttribArray (download->shader_attr_position_loc); - gl->EnableVertexAttribArray (download->shader_attr_texture_loc); - - gl->ActiveTexture (GL_TEXTURE0); - gst_gl_shader_set_uniform_1i (download->shader, "tex", 0); - gl->BindTexture (GL_TEXTURE_2D, download->in_texture); - - gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); - - gst_gl_context_clear_shader (context); - - gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2], - viewport_dim[3]); - v_format = GST_VIDEO_INFO_FORMAT (&download->info); + GST_TRACE ("doing YUV download of texture:%u (%ux%u)", + download->in_texture, out_width, out_height); + + download->priv->result = + gst_gl_color_convert_perform (download->convert, in_tex, + download->out_texture); + if (!download->priv->result) + return; + + gl->BindFramebuffer (GL_FRAMEBUFFER, download->convert->fbo); + gl->ReadBuffer (GL_COLOR_ATTACHMENT0); + switch (v_format) { + case GST_VIDEO_FORMAT_AYUV: + gl->ReadPixels (0, 0, out_width, out_height, GL_RGBA, + GL_UNSIGNED_BYTE, download->data[0]); + break; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, out_height, GL_RGBA, + GL_UNSIGNED_BYTE, download->data[0]); + break; + case GST_VIDEO_FORMAT_I420: + { + gl->ReadPixels (0, 0, out_width, out_height, GL_LUMINANCE, + GL_UNSIGNED_BYTE, download->data[0]); + + gl->ReadBuffer (GL_COLOR_ATTACHMENT1); + gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, + GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, + download->data[1]); + + gl->ReadBuffer (GL_COLOR_ATTACHMENT2); + gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, + GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, + download->data[2]); + } + break; + case GST_VIDEO_FORMAT_YV12: + { + gl->ReadPixels (0, 0, out_width, out_height, GL_LUMINANCE, + GL_UNSIGNED_BYTE, download->data[0]); + + gl->ReadBuffer (GL_COLOR_ATTACHMENT1); + gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, + GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, + download->data[2]); + + gl->ReadBuffer (GL_COLOR_ATTACHMENT2); + gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, + GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, + download->data[1]); + } + break; case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_xRGB: @@ -1072,428 +441,6 @@ _do_download_draw_rgb_gles2 (GstGLContext * context, GstGLDownload * download) gl->ReadPixels (0, 0, out_width, out_height, GL_RGB, GL_UNSIGNED_BYTE, download->data[0]); break; - default: - gst_gl_context_set_error (context, - "Download video format inconsistency %d", v_format); - g_assert_not_reached (); - break; - } - - gst_gl_context_check_framebuffer_status (context); - gl->BindFramebuffer (GL_FRAMEBUFFER, 0); -} -#endif - -#if GST_GL_HAVE_OPENGL -static void -_do_download_draw_yuv_opengl (GstGLContext * context, GstGLDownload * download) -{ - GstGLFuncs *gl; - GstVideoFormat v_format; - guint out_width = GST_VIDEO_INFO_WIDTH (&download->info); - guint out_height = GST_VIDEO_INFO_HEIGHT (&download->info); - const gfloat *cms_offset; - const gfloat *cms_ycoeff; - const gfloat *cms_ucoeff; - const gfloat *cms_vcoeff; - - GLenum multipleRT[] = { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2 - }; - - gfloat verts[8] = { 1.0f, -1.0f, - -1.0f, -1.0f, - -1.0f, 1.0f, - 1.0f, 1.0f - }; - gfloat texcoords[8] = { 1.0, 0.0, - 0.0, 0.0, - 0.0, 1.0, - 1.0, 1.0 - }; - - gl = context->gl_vtable; - - v_format = GST_VIDEO_INFO_FORMAT (&download->info); - - GST_TRACE ("doing YUV download of texture:%u (%ux%u) using fbo:%u", - download->in_texture, out_width, out_height, download->fbo); - - gl->BindFramebuffer (GL_FRAMEBUFFER, download->fbo); - - gl->PushAttrib (GL_VIEWPORT_BIT); - - gl->MatrixMode (GL_PROJECTION); - gl->PushMatrix (); - gl->LoadIdentity (); - gluOrtho2D (0.0, out_width, 0.0, out_height); - - gl->MatrixMode (GL_MODELVIEW); - gl->PushMatrix (); - gl->LoadIdentity (); - - gl->Viewport (0, 0, out_width, out_height); - - switch (v_format) { - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - case GST_VIDEO_FORMAT_AYUV: - { - gl->DrawBuffer (GL_COLOR_ATTACHMENT0); - - gl->ClearColor (0.0, 0.0, 0.0, 0.0); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - gst_gl_shader_use (download->shader); - - gl->MatrixMode (GL_PROJECTION); - gl->LoadIdentity (); - - gl->ActiveTexture (GL_TEXTURE0); - gst_gl_shader_set_uniform_1i (download->shader, "tex", 0); - gst_gl_shader_set_uniform_1f (download->shader, "width", out_width); - gl->BindTexture (GL_TEXTURE_2D, download->in_texture); - } - break; - - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - { - gl->DrawBuffers (3, multipleRT); - - gl->ClearColor (0.0, 0.0, 0.0, 0.0); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - gst_gl_shader_use (download->shader); - - gl->MatrixMode (GL_PROJECTION); - gl->LoadIdentity (); - - gl->ActiveTexture (GL_TEXTURE0); - gst_gl_shader_set_uniform_1i (download->shader, "tex", 0); - gst_gl_shader_set_uniform_1f (download->shader, "w", (gfloat) out_width); - gst_gl_shader_set_uniform_1f (download->shader, "h", (gfloat) out_height); - gl->BindTexture (GL_TEXTURE_2D, download->in_texture); - } - break; - - default: - break; - gst_gl_context_set_error (context, - "Download video format inconsistensy %d", v_format); - } - - if (gst_video_colorimetry_matches (&download->info.colorimetry, - GST_VIDEO_COLORIMETRY_BT709)) { - cms_offset = bt709_offset; - cms_ycoeff = bt709_ycoeff; - cms_ucoeff = bt709_ucoeff; - cms_vcoeff = bt709_vcoeff; - } else if (gst_video_colorimetry_matches (&download->info.colorimetry, - GST_VIDEO_COLORIMETRY_BT601)) { - cms_offset = bt601_offset; - cms_ycoeff = bt601_ycoeff; - cms_ucoeff = bt601_ucoeff; - cms_vcoeff = bt601_vcoeff; - } else { - /* defaults */ - cms_offset = bt601_offset; - cms_ycoeff = bt601_ycoeff; - cms_ucoeff = bt601_ucoeff; - cms_vcoeff = bt601_vcoeff; - } - - gst_gl_shader_set_uniform_3fv (download->shader, "offset", 1, - (gfloat *) cms_offset); - gst_gl_shader_set_uniform_3fv (download->shader, "ycoeff", 1, - (gfloat *) cms_ycoeff); - gst_gl_shader_set_uniform_3fv (download->shader, "ucoeff", 1, - (gfloat *) cms_ucoeff); - gst_gl_shader_set_uniform_3fv (download->shader, "vcoeff", 1, - (gfloat *) cms_vcoeff); - - gl->ClientActiveTexture (GL_TEXTURE0); - - gl->EnableClientState (GL_VERTEX_ARRAY); - gl->EnableClientState (GL_TEXTURE_COORD_ARRAY); - - gl->VertexPointer (2, GL_FLOAT, 0, &verts); - gl->TexCoordPointer (2, GL_FLOAT, 0, &texcoords); - - gl->DrawArrays (GL_QUADS, 0, 4); - - gl->DisableClientState (GL_VERTEX_ARRAY); - gl->DisableClientState (GL_TEXTURE_COORD_ARRAY); - - gl->DrawBuffer (GL_NONE); - - /* don't check if GLSL is available - * because download yuv is not available - * without GLSL (whereas rgb is) - */ - gl->UseProgramObject (0); - - gl->Disable (GL_TEXTURE_2D); - gl->MatrixMode (GL_PROJECTION); - gl->PopMatrix (); - gl->MatrixMode (GL_MODELVIEW); - gl->PopMatrix (); - gl->PopAttrib (); - - gst_gl_context_check_framebuffer_status (context); - - gl->BindFramebuffer (GL_FRAMEBUFFER, download->fbo); - gl->ReadBuffer (GL_COLOR_ATTACHMENT0); - - switch (v_format) { - case GST_VIDEO_FORMAT_AYUV: -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - gl->ReadPixels (0, 0, out_width, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8, download->data[0]); -#else - gl->ReadPixels (0, 0, out_width, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, download->data[0]); -#endif - break; - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, download->data[0]); -#else - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8, download->data[0]); -#endif - break; - case GST_VIDEO_FORMAT_I420: - { - gl->ReadPixels (0, 0, out_width, out_height, GL_LUMINANCE, - GL_UNSIGNED_BYTE, download->data[0]); - - gl->ReadBuffer (GL_COLOR_ATTACHMENT1); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[1]); - - gl->ReadBuffer (GL_COLOR_ATTACHMENT2); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[2]); - } - break; - case GST_VIDEO_FORMAT_YV12: - { - gl->ReadPixels (0, 0, out_width, out_height, GL_LUMINANCE, - GL_UNSIGNED_BYTE, download->data[0]); - - gl->ReadBuffer (GL_COLOR_ATTACHMENT1); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[2]); - - gl->ReadBuffer (GL_COLOR_ATTACHMENT2); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[1]); - } - break; - default: - break; - gst_gl_context_set_error (context, - "Download video format inconsistensy %d", v_format); - g_assert_not_reached (); - } - gl->ReadBuffer (GL_NONE); - - gst_gl_context_check_framebuffer_status (context); - - gl->BindFramebuffer (GL_FRAMEBUFFER, 0); -} -#endif - -#if GST_GL_HAVE_GLES2 -static void -_do_download_draw_yuv_gles2 (GstGLContext * context, GstGLDownload * download) -{ - GstGLFuncs *gl; - GstVideoFormat v_format; - guint out_width, out_height; - const gfloat *cms_offset; - const gfloat *cms_ycoeff; - const gfloat *cms_ucoeff; - const gfloat *cms_vcoeff; - - GLint viewport_dim[4]; - - const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f, - 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 0.0f, .0f, - -1.0f, 1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f - }; - - GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; - - gl = context->gl_vtable; - - out_width = GST_VIDEO_INFO_WIDTH (&download->info); - out_height = GST_VIDEO_INFO_HEIGHT (&download->info); - v_format = GST_VIDEO_INFO_FORMAT (&download->info); - - GST_TRACE ("doing YUV download of texture:%u (%ux%u) using fbo:%u", - download->in_texture, out_width, out_height, download->fbo); - - gst_gl_context_check_framebuffer_status (context); - gl->BindFramebuffer (GL_FRAMEBUFFER, download->fbo); - - gl->GetIntegerv (GL_VIEWPORT, viewport_dim); - - gl->Viewport (0, 0, out_width, out_height); - - switch (v_format) { - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - case GST_VIDEO_FORMAT_AYUV: - { - gl->ClearColor (0.0, 0.0, 0.0, 0.0); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - gst_gl_shader_use (download->shader); - - gl->VertexAttribPointer (download->shader_attr_position_loc, 3, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - gl->VertexAttribPointer (download->shader_attr_texture_loc, 2, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - - gl->EnableVertexAttribArray (download->shader_attr_position_loc); - gl->EnableVertexAttribArray (download->shader_attr_texture_loc); - - gl->ActiveTexture (GL_TEXTURE0); - gst_gl_shader_set_uniform_1i (download->shader, "tex", 0); - gl->BindTexture (GL_TEXTURE_2D, download->in_texture); - } - break; - - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - { - gl->ClearColor (0.0, 0.0, 0.0, 0.0); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - gst_gl_shader_use (download->shader); - - gl->ActiveTexture (GL_TEXTURE0); - gst_gl_shader_set_uniform_1i (download->shader, "tex", 0); - gst_gl_shader_set_uniform_1f (download->shader, "w", (gfloat) out_width); - gst_gl_shader_set_uniform_1f (download->shader, "h", (gfloat) out_height); - gl->BindTexture (GL_TEXTURE_2D, download->in_texture); - } - break; - - default: - break; - gst_gl_context_set_error (context, - "Download video format inconsistensy %d", v_format); - - } - - if (gst_video_colorimetry_matches (&download->info.colorimetry, - GST_VIDEO_COLORIMETRY_BT709)) { - cms_offset = bt709_offset; - cms_ycoeff = bt709_ycoeff; - cms_ucoeff = bt709_ucoeff; - cms_vcoeff = bt709_vcoeff; - } else if (gst_video_colorimetry_matches (&download->info.colorimetry, - GST_VIDEO_COLORIMETRY_BT601)) { - cms_offset = bt601_offset; - cms_ycoeff = bt601_ycoeff; - cms_ucoeff = bt601_ucoeff; - cms_vcoeff = bt601_vcoeff; - } else { - /* defaults */ - cms_offset = bt601_offset; - cms_ycoeff = bt601_ycoeff; - cms_ucoeff = bt601_ucoeff; - cms_vcoeff = bt601_vcoeff; - } - - gst_gl_shader_set_uniform_3fv (download->shader, "offset", 1, - (gfloat *) cms_offset); - gst_gl_shader_set_uniform_3fv (download->shader, "ycoeff", 1, - (gfloat *) cms_ycoeff); - gst_gl_shader_set_uniform_3fv (download->shader, "ucoeff", 1, - (gfloat *) cms_ucoeff); - gst_gl_shader_set_uniform_3fv (download->shader, "vcoeff", 1, - (gfloat *) cms_vcoeff); - - gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); - - /* don't check if GLSL is available - * because download yuv is not available - * without GLSL (whereas rgb is) - */ - gst_gl_context_clear_shader (context); - - gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2], - viewport_dim[3]); - - switch (v_format) { - case GST_VIDEO_FORMAT_AYUV: -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - gl->ReadPixels (0, 0, out_width, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8, download->data[0]); -#else - gl->ReadPixels (0, 0, out_width, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, download->data[0]); -#endif - break; - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, download->data[0]); -#else - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, out_height, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8, download->data[0]); -#endif - break; - case GST_VIDEO_FORMAT_I420: - { - gl->ReadPixels (0, 0, out_width, out_height, GL_LUMINANCE, - GL_UNSIGNED_BYTE, download->data[0]); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[1]); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[2]); - } - break; - case GST_VIDEO_FORMAT_YV12: - { - gl->ReadPixels (0, 0, out_width, out_height, GL_LUMINANCE, - GL_UNSIGNED_BYTE, download->data[0]); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[2]); - - gl->ReadPixels (0, 0, GST_ROUND_UP_2 (out_width) / 2, - GST_ROUND_UP_2 (out_height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, - download->data[1]); - } - break; default: break; gst_gl_context_set_error (context, @@ -1504,4 +451,3 @@ _do_download_draw_yuv_gles2 (GstGLContext * context, GstGLDownload * download) gst_gl_context_check_framebuffer_status (context); gl->BindFramebuffer (GL_FRAMEBUFFER, 0); } -#endif diff --git a/gst-libs/gst/gl/gstgldownload.h b/gst-libs/gst/gl/gstgldownload.h index 5b6558c898..930bbafd13 100644 --- a/gst-libs/gst/gl/gstgldownload.h +++ b/gst-libs/gst/gl/gstgldownload.h @@ -49,6 +49,7 @@ struct _GstGLDownload GMutex lock; GstGLContext *context; + GstGLColorConvert *convert; /* output data */ GstVideoInfo info; @@ -57,13 +58,8 @@ struct _GstGLDownload gboolean initted; /* used for the conversion */ - GLuint fbo; - GLuint depth_buffer; GLuint in_texture; GLuint out_texture[GST_VIDEO_MAX_PLANES]; - GstGLShader *shader; - GLint shader_attr_position_loc; - GLint shader_attr_texture_loc; GstGLDownloadPrivate *priv; @@ -81,21 +77,6 @@ struct _GstGLDownloadClass GObjectClass object_class; }; -/** - * GST_GL_DOWNLOAD_FORMATS: - * - * The currently supported formats that can be downloaded - */ -# define GST_GL_DOWNLOAD_FORMATS "{ RGB, RGBx, RGBA, BGR, BGRx, BGRA, xRGB, " \ - "xBGR, ARGB, ABGR, I420, YV12, YUY2, UYVY, AYUV }" - -/** - * GST_GL_DOWNLOAD_VIDEO_CAPS: - * - * The currently supported #GstCaps that can be downloaded - */ -#define GST_GL_DOWNLOAD_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_GL_DOWNLOAD_FORMATS) - GstGLDownload * gst_gl_download_new (GstGLContext * context); gboolean gst_gl_download_init_format (GstGLDownload * download, GstVideoFormat v_format, diff --git a/gst-libs/gst/gl/gstglfilter.c b/gst-libs/gst/gl/gstglfilter.c index 92bbf455f3..bd4a9883d5 100644 --- a/gst-libs/gst/gl/gstglfilter.c +++ b/gst-libs/gst/gl/gstglfilter.c @@ -43,7 +43,7 @@ static GstStaticPadTemplate gst_gl_filter_src_pad_template = GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, "RGBA") "; " #endif - GST_VIDEO_CAPS_MAKE (GST_GL_DOWNLOAD_FORMATS) "; " + GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS) "; " GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA")) @@ -58,7 +58,7 @@ static GstStaticPadTemplate gst_gl_filter_sink_pad_template = GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, "RGBA") "; " #endif - GST_VIDEO_CAPS_MAKE (GST_GL_UPLOAD_FORMATS) "; " + GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS) "; " GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA")) diff --git a/gst-libs/gst/gl/gstglmixer.c b/gst-libs/gst/gl/gstglmixer.c index bab8016101..782a2bea3f 100644 --- a/gst-libs/gst/gl/gstglmixer.c +++ b/gst-libs/gst/gl/gstglmixer.c @@ -533,7 +533,7 @@ enum static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_DOWNLOAD_FORMATS) "; " + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS) "; " GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA")) @@ -542,7 +542,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_UPLOAD_FORMATS) "; " + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS) "; " GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA")) diff --git a/gst-libs/gst/gl/gstglupload.c b/gst-libs/gst/gl/gstglupload.c index 667322cbc4..882928eb81 100644 --- a/gst-libs/gst/gl/gstglupload.c +++ b/gst-libs/gst/gl/gstglupload.c @@ -47,13 +47,11 @@ static void _do_upload (GstGLContext * context, GstGLUpload * upload); static gboolean _do_upload_fill (GstGLContext * context, GstGLUpload * upload); static gboolean _do_upload_make (GstGLContext * context, GstGLUpload * upload); static void _init_upload (GstGLContext * context, GstGLUpload * upload); -static gboolean _init_upload_fbo (GstGLContext * context, GstGLUpload * upload); +//static gboolean _init_upload_fbo (GstGLContext * context, GstGLUpload * upload); static gboolean _gst_gl_upload_perform_with_data_unlocked (GstGLUpload * upload, GLuint texture_id, gpointer data[GST_VIDEO_MAX_PLANES]); static void _do_upload_with_meta (GstGLContext * context, GstGLUpload * upload); -static gboolean _do_upload_draw (GstGLContext * context, GstGLUpload * upload); - /* *INDENT-OFF* */ #define YUV_TO_RGB_COEFFICIENTS \ @@ -63,206 +61,10 @@ static gboolean _do_upload_draw (GstGLContext * context, GstGLUpload * upload); "uniform vec3 bcoeff;\n" /* FIXME: use the colormatrix support from videoconvert */ - -/* BT. 601 standard with the following ranges: - * Y = [16..235] (of 255) - * Cb/Cr = [16..240] (of 255) - */ -static const gfloat bt601_offset[] = {-0.0625, -0.5, -0.5}; -static const gfloat bt601_rcoeff[] = {1.164, 0.000, 1.596}; -static const gfloat bt601_gcoeff[] = {1.164,-0.391,-0.813}; -static const gfloat bt601_bcoeff[] = {1.164, 2.018, 0.000}; - -/* BT. 709 standard with the following ranges: - * Y = [16..235] (of 255) - * Cb/Cr = [16..240] (of 255) - */ -static const gfloat bt709_offset[] = {-0.0625, -0.5, -0.5}; -static const gfloat bt709_rcoeff[] = {1.164, 0.000, 1.787}; -static const gfloat bt709_gcoeff[] = {1.164,-0.213,-0.531}; -static const gfloat bt709_bcoeff[] = {1.164,-2.112, 0.000}; - -/** GRAY16 to RGB conversion - * data transfered as GL_LUMINANCE_ALPHA then convert back to GRAY16 - * high byte weight as : 255*256/65535 - * ([0~1] denormalize to [0~255],shift to high byte,normalize to [0~1]) - * low byte weight as : 255/65535 (similar) - * */ -#define COMPOSE_WEIGHT \ - "const vec2 compose_weight = vec2(0.996109, 0.003891);\n" - -/* Channel reordering for XYZ <-> ZYX conversion */ -static const char *frag_REORDER = { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - "void main(void)\n" - "{\n" - " vec4 t = texture2D(tex, v_texcoord * tex_scale0);\n" - " gl_FragColor = vec4(t.%c, t.%c, t.%c, 1.0);\n" - "}" -}; - -/** GRAY16 to RGB conversion - * data transfered as GL_LUMINANCE_ALPHA then convert back to GRAY16 - * high byte weight as : 255*256/65535 - * ([0~1] denormalize to [0~255],shift to high byte,normalize to [0~1]) - * low byte weight as : 255/65535 (similar) - * */ -static const char *frag_COMPOSE = { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - COMPOSE_WEIGHT - "void main(void)\n" - "{\n" - " vec4 t = texture2D(tex, v_texcoord * tex_scale0);\n" - " float value = dot(t.%c%c, compose_weight);" - " gl_FragColor = vec4(value, value, value, 1.0);\n" - "}" - -}; -static const char *frag_AYUV = { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - YUV_TO_RGB_COEFFICIENTS - "void main(void) {\n" - " float r,g,b;\n" - " vec3 yuv;\n" - " yuv = texture2D(tex,v_texcoord * tex_scale0).gba;\n" - " yuv += offset;\n" - " r = dot(yuv, rcoeff);\n" - " g = dot(yuv, gcoeff);\n" - " b = dot(yuv, bcoeff);\n" - " gl_FragColor=vec4(r,g,b,1.0);\n" - "}" -}; - -/** YUV to RGB conversion */ -static const char *frag_PLANAR_YUV = { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D Ytex,Utex,Vtex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - YUV_TO_RGB_COEFFICIENTS - "void main(void) {\n" - " float r,g,b;\n" - " vec3 yuv;\n" - " yuv.x=texture2D(Ytex,v_texcoord * tex_scale0).r;\n" - " yuv.y=texture2D(Utex,v_texcoord * tex_scale1).r;\n" - " yuv.z=texture2D(Vtex,v_texcoord * tex_scale2).r;\n" - " yuv += offset;\n" - " r = dot(yuv, rcoeff);\n" - " g = dot(yuv, gcoeff);\n" - " b = dot(yuv, bcoeff);\n" - " gl_FragColor=vec4(r,g,b,1.0);\n" - "}" -}; - -/** NV12/NV21 to RGB conversion */ -static const char *frag_NV12_NV21 = { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D Ytex,UVtex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - YUV_TO_RGB_COEFFICIENTS - "void main(void) {\n" - " float r,g,b;\n" - " vec3 yuv;\n" - " yuv.x=texture2D(Ytex, v_texcoord * tex_scale0).r;\n" - " yuv.yz=texture2D(UVtex, v_texcoord * tex_scale1).%c%c;\n" - " yuv += offset;\n" - " r = dot(yuv, rcoeff);\n" - " g = dot(yuv, gcoeff);\n" - " b = dot(yuv, bcoeff);\n" - " gl_FragColor=vec4(r,g,b,1.0);\n" - "}" -}; - -/* Direct fragments copy with stride-scaling */ -static const char *frag_COPY = { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - "void main(void)\n" - "{\n" - " vec4 t = texture2D(tex, v_texcoord * tex_scale0);\n" - " gl_FragColor = vec4(t.rgb, 1.0);\n" - "}" -}; - -/* YUY2:r,g,a - UYVY:a,b,r */ -static const gchar *frag_YUY2_UYVY = - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D Ytex, UVtex;\n" - "uniform vec2 tex_scale0;\n" - "uniform vec2 tex_scale1;\n" - "uniform vec2 tex_scale2;\n" - YUV_TO_RGB_COEFFICIENTS - "void main(void) {\n" - " vec3 yuv;\n" - " float fx, fy, y, u, v, r, g, b;\n" - " fx = v_texcoord.x;\n" - " fy = v_texcoord.y;\n" - " yuv.x = texture2D(Ytex,v_texcoord * tex_scale0).%c;\n" - " yuv.yz = texture2D(UVtex,v_texcoord * tex_scale1).%c%c;\n" - " yuv+=offset;\n" - " r = dot(yuv, rcoeff);\n" - " g = dot(yuv, gcoeff);\n" - " b = dot(yuv, bcoeff);\n" - " gl_FragColor = vec4(r, g, b, 1.0);\n" - "}\n"; - -static const gchar *text_vertex_shader = - "attribute vec4 a_position; \n" - "attribute vec2 a_texcoord; \n" - "varying vec2 v_texcoord; \n" - "void main() \n" - "{ \n" - " gl_Position = a_position; \n" - " v_texcoord = a_texcoord; \n" - "} \n"; - -/* *INDENT-ON* */ - struct TexData { - guint internal_format, format, type, width, height; + guint format, type, width, height; gfloat tex_scaling[2]; - const gchar *shader_name; guint unpack_length; }; @@ -271,17 +73,6 @@ struct _GstGLUploadPrivate int n_textures; gboolean result; - const gchar *YUY2_UYVY; - const gchar *PLANAR_YUV; - const gchar *AYUV; - const gchar *NV12_NV21; - const gchar *REORDER; - const gchar *COPY; - const gchar *COMPOSE; - const gchar *vert_shader; - - gboolean (*draw) (GstGLContext * context, GstGLUpload * download); - struct TexData texture_info[GST_VIDEO_MAX_PLANES]; GstBuffer *buffer; @@ -319,14 +110,6 @@ gst_gl_upload_init (GstGLUpload * upload) upload->context = NULL; g_mutex_init (&upload->lock); - - upload->fbo = 0; - upload->depth_buffer = 0; - upload->out_texture = 0; - upload->shader = NULL; - - upload->shader_attr_position_loc = 0; - upload->shader_attr_texture_loc = 0; } /** @@ -339,22 +122,11 @@ GstGLUpload * gst_gl_upload_new (GstGLContext * context) { GstGLUpload *upload; - GstGLUploadPrivate *priv; upload = g_object_new (GST_TYPE_GL_UPLOAD, NULL); upload->context = gst_object_ref (context); - priv = upload->priv; - - priv->YUY2_UYVY = frag_YUY2_UYVY; - priv->PLANAR_YUV = frag_PLANAR_YUV; - priv->AYUV = frag_AYUV; - priv->REORDER = frag_REORDER; - priv->COMPOSE = frag_COMPOSE; - priv->COPY = frag_COPY; - priv->NV12_NV21 = frag_NV12_NV21; - priv->vert_shader = text_vertex_shader; - priv->draw = _do_upload_draw; + upload->convert = gst_gl_color_convert_new (context); return upload; } @@ -363,32 +135,11 @@ static void gst_gl_upload_finalize (GObject * object) { GstGLUpload *upload; - guint i; upload = GST_GL_UPLOAD (object); - for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { - if (upload->in_texture[i]) { - gst_gl_context_del_texture (upload->context, &upload->in_texture[i]); - upload->in_texture[i] = 0; - } - } - if (upload->out_texture) { - gst_gl_context_del_texture (upload->context, &upload->out_texture); - upload->out_texture = 0; - } - if (upload->priv->tex_id) { - gst_gl_context_del_texture (upload->context, &upload->priv->tex_id); - upload->priv->tex_id = 0; - } - if (upload->fbo || upload->depth_buffer) { - gst_gl_context_del_fbo (upload->context, upload->fbo, upload->depth_buffer); - upload->fbo = 0; - upload->depth_buffer = 0; - } - if (upload->shader) { - gst_object_unref (upload->shader); - upload->shader = NULL; + if (upload->convert) { + gst_object_unref (upload->convert); } if (upload->context) { @@ -403,7 +154,7 @@ gst_gl_upload_finalize (GObject * object) static gboolean _gst_gl_upload_init_format_unlocked (GstGLUpload * upload, - GstVideoInfo * in_info, GstVideoInfo * out_info) + GstVideoInfo *in_info) { g_return_val_if_fail (upload != NULL, FALSE); g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (in_info) != @@ -413,12 +164,9 @@ _gst_gl_upload_init_format_unlocked (GstGLUpload * upload, if (upload->initted) { return FALSE; - } else { - upload->initted = TRUE; } upload->in_info = *in_info; - upload->out_info = *out_info; gst_gl_context_thread_add (upload->context, (GstGLContextThreadFunc) _init_upload, upload); @@ -431,7 +179,6 @@ _gst_gl_upload_init_format_unlocked (GstGLUpload * upload, * gst_gl_upload_init_format: * @upload: a #GstGLUpload * @in_info: input #GstVideoInfo - * @out_info: output #GstVideoInfo * * Initializes @upload with the information required for upload. * @@ -445,7 +192,7 @@ gst_gl_upload_init_format (GstGLUpload * upload, GstVideoInfo * in_info, g_mutex_lock (&upload->lock); - ret = _gst_gl_upload_init_format_unlocked (upload, in_info, out_info); + ret = _gst_gl_upload_init_format_unlocked (upload, in_info); g_mutex_unlock (&upload->lock); @@ -469,6 +216,7 @@ gst_gl_upload_perform_with_buffer (GstGLUpload * upload, GstBuffer * buffer, { GstMemory *mem; GstVideoGLTextureUploadMeta *gl_tex_upload_meta; + guint texture_ids[] = { 0, 0, 0, 0 }; g_return_val_if_fail (upload != NULL, FALSE); g_return_val_if_fail (buffer != NULL, FALSE); @@ -502,7 +250,6 @@ gst_gl_upload_perform_with_buffer (GstGLUpload * upload, GstBuffer * buffer, /* GstVideoGLTextureUploadMeta */ gl_tex_upload_meta = gst_buffer_get_video_gl_texture_upload_meta (buffer); if (gl_tex_upload_meta) { - guint texture_ids[] = { 0, 0, 0, 0 }; GST_LOG_OBJECT (upload, "Attempting upload with " "GstVideoGLTextureUploadMeta"); texture_ids[0] = upload->priv->tex_id; @@ -606,7 +353,7 @@ static gboolean _do_upload_for_meta (GstGLUpload * upload, GstVideoGLTextureUploadMeta * meta) { GstVideoMeta *v_meta; - GstVideoInfo in_info, out_info; + GstVideoInfo in_info; GstVideoFrame frame; GstMemory *mem; gboolean ret; @@ -628,9 +375,8 @@ _do_upload_for_meta (GstGLUpload * upload, GstVideoGLTextureUploadMeta * meta) height = v_meta->height; gst_video_info_set_format (&in_info, v_format, width, height); - gst_video_info_set_format (&out_info, GST_VIDEO_FORMAT_RGBA, width, height); - if (!_gst_gl_upload_init_format_unlocked (upload, &in_info, &out_info)) + if (!_gst_gl_upload_init_format_unlocked (upload, &in_info)) return FALSE; } @@ -845,42 +591,13 @@ _gst_gl_upload_perform_with_data_unlocked (GstGLUpload * upload, return TRUE; } -static gboolean -_create_shader (GstGLContext * context, const gchar * vertex_src, - const gchar * fragment_src, GstGLShader ** out_shader) -{ - GstGLShader *shader; - GError *error = NULL; - - g_return_val_if_fail (vertex_src != NULL || fragment_src != NULL, FALSE); - - shader = gst_gl_shader_new (context); - - if (vertex_src) - gst_gl_shader_set_vertex_source (shader, vertex_src); - if (fragment_src) - gst_gl_shader_set_fragment_source (shader, fragment_src); - - if (!gst_gl_shader_compile (shader, &error)) { - gst_gl_context_set_error (context, "%s", error->message); - g_error_free (error); - gst_gl_context_clear_shader (context); - gst_object_unref (shader); - return FALSE; - } - - *out_shader = shader; - return TRUE; -} - /* Called in the gl thread */ void _init_upload (GstGLContext * context, GstGLUpload * upload) { GstGLFuncs *gl; GstVideoFormat v_format; - gchar *frag_prog = NULL; - gboolean free_frag_prog, res; + GstVideoInfo out_info; gl = context->gl_vtable; @@ -895,102 +612,47 @@ _init_upload (GstGLContext * context, GstGLUpload * upload) goto error; } - if (!_init_upload_fbo (context, upload)) { + gst_video_info_set_format (&out_info, GST_VIDEO_FORMAT_RGBA, GST_VIDEO_INFO_WIDTH (&upload->in_info), GST_VIDEO_INFO_HEIGHT (&upload->in_info)); + + if (!gst_gl_color_convert_init_format (upload->convert, upload->in_info, out_info)) goto error; - } switch (v_format) { + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_RGB16: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_GRAY8: + case GST_VIDEO_FORMAT_GRAY16_BE: + case GST_VIDEO_FORMAT_GRAY16_LE: case GST_VIDEO_FORMAT_AYUV: - frag_prog = (gchar *) upload->priv->AYUV; - free_frag_prog = FALSE; upload->priv->n_textures = 1; break; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_NV21: + upload->priv->n_textures = 2; + break; case GST_VIDEO_FORMAT_Y444: case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y41B: - frag_prog = (gchar *) upload->priv->PLANAR_YUV; - free_frag_prog = FALSE; upload->priv->n_textures = 3; break; - case GST_VIDEO_FORMAT_NV12: - frag_prog = g_strdup_printf (upload->priv->NV12_NV21, 'r', 'a'); - free_frag_prog = TRUE; - upload->priv->n_textures = 2; - break; - case GST_VIDEO_FORMAT_NV21: - frag_prog = g_strdup_printf (upload->priv->NV12_NV21, 'a', 'r'); - free_frag_prog = TRUE; - upload->priv->n_textures = 2; - break; - case GST_VIDEO_FORMAT_BGR: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_BGRA: - frag_prog = g_strdup_printf (upload->priv->REORDER, 'b', 'g', 'r'); - free_frag_prog = TRUE; - upload->priv->n_textures = 1; - break; - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_ARGB: - frag_prog = g_strdup_printf (upload->priv->REORDER, 'g', 'b', 'a'); - free_frag_prog = TRUE; - upload->priv->n_textures = 1; - break; - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_ABGR: - frag_prog = g_strdup_printf (upload->priv->REORDER, 'a', 'b', 'g'); - free_frag_prog = TRUE; - upload->priv->n_textures = 1; - break; - case GST_VIDEO_FORMAT_GRAY16_BE: - frag_prog = g_strdup_printf (upload->priv->COMPOSE, 'r', 'a'); - free_frag_prog = TRUE; - upload->priv->n_textures = 1; - break; - case GST_VIDEO_FORMAT_GRAY16_LE: - frag_prog = g_strdup_printf (upload->priv->COMPOSE, 'a', 'r'); - free_frag_prog = TRUE; - upload->priv->n_textures = 1; - break; - case GST_VIDEO_FORMAT_GRAY8: - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_RGB16: - frag_prog = (gchar *) upload->priv->COPY; - free_frag_prog = FALSE; - upload->priv->n_textures = 1; - break; - case GST_VIDEO_FORMAT_YUY2: - frag_prog = g_strdup_printf (upload->priv->YUY2_UYVY, 'r', 'g', 'a'); - free_frag_prog = TRUE; - upload->priv->n_textures = 2; - break; - case GST_VIDEO_FORMAT_UYVY: - frag_prog = g_strdup_printf (upload->priv->YUY2_UYVY, 'a', 'r', 'b'); - free_frag_prog = TRUE; - upload->priv->n_textures = 2; - break; default: g_assert_not_reached (); break; } - res = - _create_shader (context, upload->priv->vert_shader, frag_prog, - &upload->shader); - if (free_frag_prog) - g_free (frag_prog); - frag_prog = NULL; - if (!res) - goto error; - - upload->shader_attr_position_loc = - gst_gl_shader_get_attribute_location (upload->shader, "a_position"); - upload->shader_attr_texture_loc = - gst_gl_shader_get_attribute_location (upload->shader, "a_texcoord"); - if (!_do_upload_make (context, upload)) goto error; @@ -1001,104 +663,24 @@ error: upload->priv->result = FALSE; } - -/* called by _init_upload (in the gl thread) */ -gboolean -_init_upload_fbo (GstGLContext * context, GstGLUpload * upload) -{ - GstGLFuncs *gl; - guint out_width, out_height; - GLuint fake_texture = 0; /* a FBO must hava texture to init */ - - gl = context->gl_vtable; - - out_width = GST_VIDEO_INFO_WIDTH (&upload->out_info); - out_height = GST_VIDEO_INFO_HEIGHT (&upload->out_info); - - if (!gl->GenFramebuffers) { - /* turn off the pipeline because Frame buffer object is a not present */ - gst_gl_context_set_error (context, - "Context, EXT_framebuffer_object supported: no"); - return FALSE; - } - - GST_INFO ("Context, EXT_framebuffer_object supported: yes"); - - /* setup FBO */ - gl->GenFramebuffers (1, &upload->fbo); - gl->BindFramebuffer (GL_FRAMEBUFFER, upload->fbo); - - /* setup the render buffer for depth */ - gl->GenRenderbuffers (1, &upload->depth_buffer); - gl->BindRenderbuffer (GL_RENDERBUFFER, upload->depth_buffer); - if (USING_OPENGL (context)) { - gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, - out_width, out_height); - gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, - out_width, out_height); - } - if (USING_GLES2 (context)) { - gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, - out_width, out_height); - } - - /* a fake texture is attached to the upload FBO (cannot init without it) */ - gl->GenTextures (1, &fake_texture); - gl->BindTexture (GL_TEXTURE_2D, fake_texture); - gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, out_width, out_height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - /* attach the texture to the FBO to renderer to */ - gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, fake_texture, 0); - - /* attach the depth render buffer to the FBO */ - gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, upload->depth_buffer); - - if (USING_OPENGL (context)) { - gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, upload->depth_buffer); - } - - if (!gst_gl_context_check_framebuffer_status (context)) { - gst_gl_context_set_error (context, "GL framebuffer status incomplete"); - return FALSE; - } - - /* unbind the FBO */ - gl->BindFramebuffer (GL_FRAMEBUFFER, 0); - - gl->DeleteTextures (1, &fake_texture); - - return TRUE; -} - /* Called by the idle function in the gl thread */ void _do_upload (GstGLContext * context, GstGLUpload * upload) { - guint in_width, in_height, out_width, out_height; + guint in_width, in_height; + guint out_texture[] = {upload->out_texture, 0, 0, 0}; - out_width = GST_VIDEO_INFO_WIDTH (&upload->out_info); - out_height = GST_VIDEO_INFO_HEIGHT (&upload->out_info); in_width = GST_VIDEO_INFO_WIDTH (&upload->in_info); in_height = GST_VIDEO_INFO_HEIGHT (&upload->in_info); - GST_TRACE ("uploading to texture:%u dimensions:%ux%u, " - "from textures:%u,%u,%u dimensions:%ux%u", upload->out_texture, - out_width, out_height, upload->in_texture[0], upload->in_texture[1], + GST_TRACE ("uploading to texture:%u with textures:%u,%u,%u dimensions:%ux%u", + upload->out_texture, upload->in_texture[0], upload->in_texture[1], upload->in_texture[2], in_width, in_height); if (!_do_upload_fill (context, upload)) goto error; - if (!upload->priv->draw (context, upload)) - goto error; + gst_gl_color_convert_perform (upload->convert, upload->in_texture, out_texture); upload->priv->result = TRUE; return; @@ -1135,6 +717,7 @@ _do_upload_make (GstGLContext * context, GstGLUpload * upload) GstVideoFormat v_format; guint in_height; struct TexData *tex = upload->priv->texture_info; + gfloat tex_scaling[GST_VIDEO_MAX_PLANES][2]; guint i; gl = context->gl_vtable; @@ -1152,97 +735,69 @@ _do_upload_make (GstGLContext * context, GstGLUpload * upload) case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_ABGR: case GST_VIDEO_FORMAT_AYUV: - tex[0].internal_format = GL_RGBA; tex[0].format = GL_RGBA; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "tex"; break; case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_BGR: - tex[0].internal_format = GL_RGB; tex[0].format = GL_RGB; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "tex"; break; case GST_VIDEO_FORMAT_GRAY8: - tex[0].internal_format = GL_LUMINANCE; tex[0].format = GL_LUMINANCE; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "tex"; break; case GST_VIDEO_FORMAT_GRAY16_BE: case GST_VIDEO_FORMAT_GRAY16_LE: - tex[0].internal_format = GL_LUMINANCE_ALPHA; tex[0].format = GL_LUMINANCE_ALPHA; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "tex"; break; case GST_VIDEO_FORMAT_YUY2: case GST_VIDEO_FORMAT_UYVY: - tex[0].internal_format = GL_LUMINANCE_ALPHA; tex[0].format = GL_LUMINANCE_ALPHA; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "Ytex"; - tex[1].internal_format = GL_RGBA8; tex[1].format = GL_RGBA; tex[1].type = GL_UNSIGNED_BYTE; tex[1].height = in_height; - tex[1].shader_name = "UVtex"; break; case GST_VIDEO_FORMAT_NV12: case GST_VIDEO_FORMAT_NV21: - tex[0].internal_format = GL_LUMINANCE; tex[0].format = GL_LUMINANCE; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "Ytex"; - tex[1].internal_format = GL_LUMINANCE_ALPHA; tex[1].format = GL_LUMINANCE_ALPHA; tex[1].type = GL_UNSIGNED_BYTE; tex[1].height = GST_ROUND_UP_2 (in_height) / 2; - tex[1].shader_name = "UVtex"; break; case GST_VIDEO_FORMAT_Y444: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y41B: - tex[0].internal_format = GL_LUMINANCE; tex[0].format = GL_LUMINANCE; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "Ytex"; - tex[1].internal_format = GL_LUMINANCE; tex[1].format = GL_LUMINANCE; tex[1].type = GL_UNSIGNED_BYTE; tex[1].height = in_height; - tex[1].shader_name = "Utex"; - tex[2].internal_format = GL_LUMINANCE; tex[2].format = GL_LUMINANCE; tex[2].type = GL_UNSIGNED_BYTE; tex[2].height = in_height; - tex[2].shader_name = "Vtex"; break; case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: - tex[0].internal_format = GL_LUMINANCE; tex[0].format = GL_LUMINANCE; tex[0].type = GL_UNSIGNED_BYTE; tex[0].height = in_height; - tex[0].shader_name = "Ytex"; - tex[1].internal_format = GL_LUMINANCE; tex[1].format = GL_LUMINANCE; tex[1].type = GL_UNSIGNED_BYTE; tex[1].height = GST_ROUND_UP_2 (in_height) / 2; - tex[1].shader_name = "Utex"; - tex[2].internal_format = GL_LUMINANCE; tex[2].format = GL_LUMINANCE; tex[2].type = GL_UNSIGNED_BYTE; tex[2].height = GST_ROUND_UP_2 (in_height) / 2; - tex[2].shader_name = "Vtex"; break; default: gst_gl_context_set_error (context, "Unsupported upload video format %d", @@ -1272,8 +827,8 @@ _do_upload_make (GstGLContext * context, GstGLUpload * upload) } tex[i].width = plane_width; - tex[i].tex_scaling[0] = 1.0f; - tex[i].tex_scaling[1] = 1.0f; + tex_scaling[i][0] = 1.0f; + tex_scaling[i][1] = 1.0f; #if GST_GL_HAVE_OPENGL || GST_GL_HAVE_GLES3 if (USING_OPENGL (context) || USING_GLES3 (context)) { @@ -1322,7 +877,7 @@ _do_upload_make (GstGLContext * context, GstGLUpload * upload) j, i, plane_stride, pstride, j, plane_stride, round_up_j); tex[i].unpack_length = j; - tex[i].tex_scaling[0] = + tex_scaling[i][0] = (gfloat) (plane_width * pstride) / (gfloat) plane_stride; break; } @@ -1342,14 +897,15 @@ _do_upload_make (GstGLContext * context, GstGLUpload * upload) gl->GenTextures (1, &upload->in_texture[i]); gl->BindTexture (GL_TEXTURE_2D, upload->in_texture[i]); - gl->TexImage2D (GL_TEXTURE_2D, 0, tex[i].internal_format, + gl->TexImage2D (GL_TEXTURE_2D, 0, tex[i].format, tex[i].width, tex[i].height, 0, tex[i].format, tex[i].type, NULL); } + gst_gl_color_convert_set_texture_scaling (upload->convert, tex_scaling); + return TRUE; } - /* called by gst_gl_context_thread_do_upload (in the gl thread) */ gboolean _do_upload_fill (GstGLContext * context, GstGLUpload * upload) @@ -1413,123 +969,3 @@ _do_upload_fill (GstGLContext * context, GstGLUpload * upload) return TRUE; } - -static gboolean -_do_upload_draw (GstGLContext * context, GstGLUpload * upload) -{ - GstGLFuncs *gl; - struct TexData *tex = upload->priv->texture_info; - guint out_width, out_height; - const gfloat *cms_offset; - const gfloat *cms_rcoeff; - const gfloat *cms_gcoeff; - const gfloat *cms_bcoeff; - gint i; - - GLint viewport_dim[4]; - - const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f, - 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 0.0f, 0.0f, - -1.0f, 1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f - }; - - GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; - - gl = context->gl_vtable; - - out_width = GST_VIDEO_INFO_WIDTH (&upload->out_info); - out_height = GST_VIDEO_INFO_HEIGHT (&upload->out_info); - - if (gst_video_colorimetry_matches (&upload->in_info.colorimetry, - GST_VIDEO_COLORIMETRY_BT709)) { - cms_offset = bt709_offset; - cms_rcoeff = bt709_rcoeff; - cms_gcoeff = bt709_gcoeff; - cms_bcoeff = bt709_bcoeff; - } else if (gst_video_colorimetry_matches (&upload->in_info.colorimetry, - GST_VIDEO_COLORIMETRY_BT601)) { - cms_offset = bt601_offset; - cms_rcoeff = bt601_rcoeff; - cms_gcoeff = bt601_gcoeff; - cms_bcoeff = bt601_bcoeff; - } else { - /* defaults */ - cms_offset = bt601_offset; - cms_rcoeff = bt601_rcoeff; - cms_gcoeff = bt601_gcoeff; - cms_bcoeff = bt601_bcoeff; - } - - gl->BindFramebuffer (GL_FRAMEBUFFER, upload->fbo); - - /* setup a texture to render to */ - gl->BindTexture (GL_TEXTURE_2D, upload->out_texture); - - /* attach the texture to the FBO to renderer to */ - gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, upload->out_texture, 0); - - gst_gl_context_clear_shader (context); - - gl->GetIntegerv (GL_VIEWPORT, viewport_dim); - - gl->Viewport (0, 0, out_width, out_height); - - gl->ClearColor (0.0, 0.0, 0.0, 0.0); - gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - gst_gl_shader_use (upload->shader); - - gl->VertexAttribPointer (upload->shader_attr_position_loc, 3, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices); - gl->VertexAttribPointer (upload->shader_attr_texture_loc, 2, - GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]); - - gl->EnableVertexAttribArray (upload->shader_attr_position_loc); - gl->EnableVertexAttribArray (upload->shader_attr_texture_loc); - - gst_gl_shader_set_uniform_3fv (upload->shader, "offset", 1, - (gfloat *) cms_offset); - gst_gl_shader_set_uniform_3fv (upload->shader, "rcoeff", 1, - (gfloat *) cms_rcoeff); - gst_gl_shader_set_uniform_3fv (upload->shader, "gcoeff", 1, - (gfloat *) cms_gcoeff); - gst_gl_shader_set_uniform_3fv (upload->shader, "bcoeff", 1, - (gfloat *) cms_bcoeff); - - for (i = upload->priv->n_textures - 1; i >= 0; i--) { - gchar *scale_name = g_strdup_printf ("tex_scale%u", i); - - gl->ActiveTexture (GL_TEXTURE0 + i); - gst_gl_shader_set_uniform_1i (upload->shader, tex[i].shader_name, i); - gst_gl_shader_set_uniform_2fv (upload->shader, scale_name, 1, - &tex[i].tex_scaling[0]); - - g_free (scale_name); - - gl->BindTexture (GL_TEXTURE_2D, upload->in_texture[i]); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - - gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); - - /* we are done with the shader */ - gst_gl_context_clear_shader (context); - - gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2], - viewport_dim[3]); - - gst_gl_context_check_framebuffer_status (context); - - gl->BindFramebuffer (GL_FRAMEBUFFER, 0); - - return TRUE; -} diff --git a/gst-libs/gst/gl/gstglupload.h b/gst-libs/gst/gl/gstglupload.h index b60d89287d..6e313ef64d 100644 --- a/gst-libs/gst/gl/gstglupload.h +++ b/gst-libs/gst/gl/gstglupload.h @@ -49,22 +49,16 @@ struct _GstGLUpload GMutex lock; GstGLContext *context; + GstGLColorConvert *convert; /* input data */ GstVideoInfo in_info; - GstVideoInfo out_info; gpointer data[GST_VIDEO_MAX_PLANES]; gboolean initted; - /* used for the conversion */ - GLuint fbo; - GLuint depth_buffer; - GLuint out_texture; GLuint in_texture[GST_VIDEO_MAX_PLANES]; - GstGLShader *shader; - GLint shader_attr_position_loc; - GLint shader_attr_texture_loc; + GLuint out_texture; /* */ GstGLUploadPrivate *priv; @@ -82,23 +76,6 @@ struct _GstGLUploadClass GObjectClass object_class; }; -/** - * GST_GL_UPLOAD_FORMATS: - * - * The currently supported formats that can be uploaded - */ -#define GST_GL_UPLOAD_FORMATS "{ RGB, RGBx, RGBA, BGR, BGRx, BGRA, xRGB, " \ - "xBGR, ARGB, ABGR, Y444, I420, YV12, Y42B, " \ - "Y41B, NV12, NV21, YUY2, UYVY, AYUV, " \ - "GRAY8, GRAY16_LE, GRAY16_BE }" - -/** - * GST_GL_UPLOAD_VIDEO_CAPS: - * - * The currently supported #GstCaps that can be uploaded - */ -#define GST_GL_UPLOAD_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_GL_UPLOAD_FORMATS) - GstGLUpload * gst_gl_upload_new (GstGLContext * context); gboolean gst_gl_upload_init_format (GstGLUpload * upload, GstVideoInfo * in_info, GstVideoInfo * out_info);