glcolorconvert: add support for the NV16 and NV61 formats
[platform/upstream/gst-plugins-base.git] / gst-libs / gst / gl / gstglcolorconvert.c
1 /*
2  * GStreamer
3  * Copyright (C) 2012-2014 Matthew Waters <ystree00@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <stdio.h>
27
28 #include "gstglcolorconvert.h"
29
30 #include "gl.h"
31 #include "gstglfuncs.h"
32 #include "gstglsl_private.h"
33
34 /**
35  * SECTION:gstglcolorconvert
36  * @title: GstGLColorConvert
37  * @short_description: convert between video color spaces and formats
38  * @see_also: #GstGLUpload, #GstGLMemory, #GstGLBaseMemory
39  *
40  * #GstGLColorConvert is an object that converts between color spaces and/or
41  * formats using OpenGL Shaders.
42  *
43  * A #GstGLColorConvert can be created with gst_gl_color_convert_new(), the
44  * configuration negotiated with gst_gl_color_convert_transform_caps() and the
45  * conversion performed with gst_gl_color_convert_perform().
46  *
47  * The glcolorconvertelement provides a GStreamer element that uses
48  * #GstGLColorConvert to convert between video formats and color spaces.
49  */
50
51 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
52 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
53 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
54 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
55 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
56
57 static void _do_convert (GstGLContext * context, GstGLColorConvert * convert);
58 static gboolean _init_convert (GstGLColorConvert * convert);
59 static gboolean _init_convert_fbo (GstGLColorConvert * convert);
60 static GstBuffer *_gst_gl_color_convert_perform_unlocked (GstGLColorConvert *
61     convert, GstBuffer * inbuf);
62
63 static gboolean _do_convert_draw (GstGLContext * context,
64     GstGLColorConvert * convert);
65
66 /* *INDENT-OFF* */
67
68 #define YUV_TO_RGB_COEFFICIENTS \
69       "uniform vec3 offset;\n" \
70       "uniform vec3 coeff1;\n" \
71       "uniform vec3 coeff2;\n" \
72       "uniform vec3 coeff3;\n"
73
74 /* FIXME: use the colormatrix support from videoconvert */
75
76 /* BT. 601 standard with the following ranges:
77  * Y = [16..235] (of 255)
78  * Cb/Cr = [16..240] (of 255)
79  */
80 static const gfloat from_yuv_bt601_offset[] = {-0.0625f, -0.5f, -0.5f};
81 static const gfloat from_yuv_bt601_rcoeff[] = {1.164f, 0.000f, 1.596f};
82 static const gfloat from_yuv_bt601_gcoeff[] = {1.164f,-0.391f,-0.813f};
83 static const gfloat from_yuv_bt601_bcoeff[] = {1.164f, 2.018f, 0.000f};
84
85 /* BT. 709 standard with the following ranges:
86  * Y = [16..235] (of 255)
87  * Cb/Cr = [16..240] (of 255)
88  */
89 static const gfloat from_yuv_bt709_offset[] = {-0.0625f, -0.5f, -0.5f};
90 static const gfloat from_yuv_bt709_rcoeff[] = {1.164f, 0.000f, 1.787f};
91 static const gfloat from_yuv_bt709_gcoeff[] = {1.164f,-0.213f,-0.531f};
92 static const gfloat from_yuv_bt709_bcoeff[] = {1.164f,2.112f, 0.000f};
93
94 #define RGB_TO_YUV_COEFFICIENTS \
95       "uniform vec3 offset;\n" \
96       "uniform vec3 coeff1;\n" \
97       "uniform vec3 coeff2;\n" \
98       "uniform vec3 coeff3;\n"
99
100 /* Matrix inverses of the color matrices found above */
101 /* BT. 601 standard with the following ranges:
102  * Y = [16..235] (of 255)
103  * Cb/Cr = [16..240] (of 255)
104  */
105 static const gfloat from_rgb_bt601_offset[] = {0.0625f, 0.5f, 0.5f};
106 static const gfloat from_rgb_bt601_ycoeff[] = {0.256816f, 0.504154f, 0.0979137f};
107 static const gfloat from_rgb_bt601_ucoeff[] = {-0.148246f, -0.29102f, 0.439266f};
108 static const gfloat from_rgb_bt601_vcoeff[] = {0.439271f, -0.367833f, -0.071438f};
109
110 /* BT. 709 standard with the following ranges:
111  * Y = [16..235] (of 255)
112  * Cb/Cr = [16..240] (of 255)
113  */
114 static const gfloat from_rgb_bt709_offset[] = {0.0625f, 0.5f, 0.5f};
115 static const gfloat from_rgb_bt709_ycoeff[] = {0.182604f, 0.614526f, 0.061976f};
116 static const gfloat from_rgb_bt709_ucoeff[] = {-0.100640f, -0.338688f, 0.439327f};
117 static const gfloat from_rgb_bt709_vcoeff[] = {0.440654f, -0.400285f, -0.040370f};
118
119 /* GRAY16 to RGB conversion
120  *  data transferred as GL_LUMINANCE_ALPHA then convert back to GRAY16
121  *  high byte weight as : 255*256/65535
122  *  ([0~1] denormalize to [0~255],shift to high byte,normalize to [0~1])
123  *  low byte weight as : 255/65535 (similar)
124  * */
125 #define COMPOSE_WEIGHT \
126     "const vec2 compose_weight = vec2(0.996109, 0.003891);\n"
127
128 #define DEFAULT_UNIFORMS         \
129     "uniform vec2 tex_scale0;\n" \
130     "uniform vec2 tex_scale1;\n" \
131     "uniform vec2 tex_scale2;\n" \
132     "uniform float width;\n"     \
133     "uniform float height;\n"    \
134     "uniform float poffset_x;\n" \
135     "uniform float poffset_y;\n"
136
137 #define MAX_FUNCTIONS 4
138
139 #define glsl_OES_extension_string "#extension GL_OES_EGL_image_external : require \n"
140
141 struct shader_templ
142 {
143   const gchar *extensions;
144   const gchar *uniforms;
145   const gchar *functions[MAX_FUNCTIONS];
146
147   GstGLTextureTarget target;
148 };
149
150 #define glsl_func_yuv_to_rgb \
151     "vec3 yuv_to_rgb (vec3 val, vec3 offset, vec3 ycoeff, vec3 ucoeff, vec3 vcoeff) {\n" \
152     "  vec3 rgb;\n"                 \
153     "  val += offset;\n"            \
154     "  rgb.r = dot(val, ycoeff);\n" \
155     "  rgb.g = dot(val, ucoeff);\n" \
156     "  rgb.b = dot(val, vcoeff);\n" \
157     "  return rgb;\n"               \
158     "}\n"
159
160 #define glsl_func_rgb_to_yuv \
161     "vec3 rgb_to_yuv (vec3 val, vec3 offset, vec3 rcoeff, vec3 gcoeff, vec3 bcoeff) {\n" \
162     "  vec3 yuv;\n"                         \
163     "  yuv.r = dot(val.rgb, rcoeff);\n"     \
164     "  yuv.g = dot(val.rgb, gcoeff);\n"     \
165     "  yuv.b = dot(val.rgb, bcoeff);\n"     \
166     "  yuv += offset;\n"                    \
167     "  return yuv;\n"                       \
168     "}\n"
169
170 /* Channel reordering for XYZ <-> ZYX conversion */
171 static const gchar templ_REORDER_BODY[] =
172     "vec4 t = texture2D(tex, texcoord * tex_scale0);\n"
173     "%s\n" /* clobber alpha channel? */
174     "gl_FragColor = vec4(t.%c, t.%c, t.%c, t.%c);\n";
175
176 static const struct shader_templ templ_REORDER =
177   { NULL,
178     DEFAULT_UNIFORMS "uniform sampler2D tex;\n",
179     { NULL, },
180     GST_GL_TEXTURE_TARGET_2D
181   };
182
183 /* GRAY16 to RGB conversion
184  *  data transferred as GL_LUMINANCE_ALPHA then convert back to GRAY16
185  *  high byte weight as : 255*256/65535
186  *  ([0~1] denormalize to [0~255],shift to high byte,normalize to [0~1])
187  *  low byte weight as : 255/65535 (similar)
188  * */
189 static const gchar templ_COMPOSE_BODY[] =
190     "vec4 rgba;\n"
191     "vec4 t = texture2D(tex, texcoord * tex_scale0);\n"
192     "rgba.rgb = vec3 (dot(t.%c%c, compose_weight));"
193     "rgba.a = 1.0;\n"
194     "gl_FragColor = vec4(rgba.%c, rgba.%c, rgba.%c, rgba.%c);\n";
195
196 static const struct shader_templ templ_COMPOSE =
197   { NULL,
198     DEFAULT_UNIFORMS COMPOSE_WEIGHT "uniform sampler2D tex;\n",
199     { NULL, },
200     GST_GL_TEXTURE_TARGET_2D
201   };
202
203 /* Shaders for AYUV and varieties */
204 static const gchar templ_AYUV_to_RGB_BODY[] =
205     "vec4 texel, rgba;\n"
206     "texel = texture2D(tex, texcoord * tex_scale0);\n"
207     "rgba.rgb = yuv_to_rgb (texel.%s, offset, coeff1, coeff2, coeff3);\n"
208     "rgba.a = texel.%c;\n"
209     "gl_FragColor=vec4(rgba.%c,rgba.%c,rgba.%c,rgba.%c);\n";
210
211 static const struct shader_templ templ_AYUV_to_RGB =
212   { NULL,
213     DEFAULT_UNIFORMS YUV_TO_RGB_COEFFICIENTS "uniform sampler2D tex;\n",
214     { glsl_func_yuv_to_rgb, NULL, },
215     GST_GL_TEXTURE_TARGET_2D
216   };
217
218 static const gchar templ_RGB_to_AYUV_BODY[] =
219     "vec4 texel, ayuv;\n"
220     "texel = texture2D(tex, texcoord).%c%c%c%c;\n"
221     "ayuv.%s = rgb_to_yuv (texel.rgb, offset, coeff1, coeff2, coeff3);\n"
222     "ayuv.%c = %s;\n"
223     "gl_FragColor = ayuv;\n";
224
225 static const struct shader_templ templ_RGB_to_AYUV =
226   { NULL,
227     DEFAULT_UNIFORMS RGB_TO_YUV_COEFFICIENTS "uniform sampler2D tex;\n",
228     { glsl_func_rgb_to_yuv, NULL, },
229     GST_GL_TEXTURE_TARGET_2D
230   };
231
232 /* YUV to RGB conversion */
233 static const gchar templ_PLANAR_YUV_to_RGB_BODY[] =
234     "vec4 texel, rgba;\n"
235     /* FIXME: should get the sampling right... */
236     "texel.x = texture2D(Ytex, texcoord * tex_scale0).r;\n"
237     "texel.y = texture2D(Utex, texcoord * tex_scale1).r;\n"
238     "texel.z = texture2D(Vtex, texcoord * tex_scale2).r;\n"
239     "rgba.rgb = yuv_to_rgb (texel.xyz, offset, coeff1, coeff2, coeff3);\n"
240     "rgba.a = 1.0;\n"
241     "gl_FragColor=vec4(rgba.%c,rgba.%c,rgba.%c,rgba.%c);\n";
242
243 static const struct shader_templ templ_PLANAR_YUV_to_RGB =
244   { NULL,
245     DEFAULT_UNIFORMS YUV_TO_RGB_COEFFICIENTS "uniform sampler2D Ytex, Utex, Vtex;\n",
246     { glsl_func_yuv_to_rgb, NULL, },
247     GST_GL_TEXTURE_TARGET_2D
248   };
249
250 static const gchar templ_RGB_to_PLANAR_YUV_BODY[] =
251     "vec4 texel;\n"
252     "vec3 yuv;\n"
253     "texel = texture2D(tex, texcoord).%c%c%c%c;\n"
254     /* FIXME: this is not quite correct yet */
255     "vec4 uv_texel = vec4(0.0);\n"
256     /* One u and v sample can be generated by a nxm sized block given by     */
257     /* @chroma_sampling.  The result is the average of all the values in the */
258     /* block computed with a rolling average. */
259     "vec2 unnormalization;\n"
260     "if (texcoord.x == v_texcoord.x) {\n"
261     "  unnormalization = vec2(width, height);\n"
262     "} else {\n"
263     "  unnormalization = vec2 (1.0);\n"
264     "}\n"
265      /* scale for chroma size */
266     "vec2 chroma_pos = texcoord * chroma_sampling * unnormalization;\n"
267      /* offset chroma to the center of the first texel in the block */
268     "chroma_pos -= clamp(chroma_sampling * 0.5 - 0.5, vec2(0.0), chroma_sampling);\n"
269     "if (chroma_pos.x < width && chroma_pos.y < height) {\n"
270     "  for (int i = 0; i < int(chroma_sampling.x); i++) {\n"
271     "    vec2 delta = vec2 (float(i), 0.0);\n"
272     "    for (int j = 0; j < int(chroma_sampling.y); j++) {\n"
273     "      int n = (i+1)*(j+1);\n"
274     "      delta.y = float(j);\n"
275     "      vec4 s = texture2D(tex, (chroma_pos + delta) / unnormalization).%c%c%c%c;\n"
276            /* rolling average */
277     "      uv_texel = (float(n-1) * uv_texel + s) / float(n);\n"
278     "    }\n"
279     "  }\n"
280     "}\n"
281     "yuv.x = rgb_to_yuv (texel.rgb, offset, coeff1, coeff2, coeff3).x;\n"
282     "yuv.yz = rgb_to_yuv (uv_texel.rgb, offset, coeff1, coeff2, coeff3).yz;\n"
283     "gl_FragData[0] = vec4(yuv.x, 0.0, 0.0, 1.0);\n"
284     "gl_FragData[1] = vec4(yuv.y, 0.0, 0.0, 1.0);\n"
285     "gl_FragData[2] = vec4(yuv.z, 0.0, 0.0, 1.0);\n";
286
287 static const struct shader_templ templ_RGB_to_PLANAR_YUV =
288   { NULL,
289     DEFAULT_UNIFORMS RGB_TO_YUV_COEFFICIENTS "uniform sampler2D tex;\n"
290     "uniform vec2 chroma_sampling;\n",
291     { glsl_func_rgb_to_yuv, NULL, },
292     GST_GL_TEXTURE_TARGET_2D
293   };
294
295 /* semi-planar to RGB conversion */
296 static const gchar templ_SEMI_PLANAR_to_RGB_BODY[] =
297     "vec4 rgba;\n"
298     "vec3 yuv;\n"
299     /* FIXME: should get the sampling right... */
300     "yuv.x=texture2D(Ytex, texcoord * tex_scale0).r;\n"
301     "yuv.yz=texture2D(UVtex, texcoord * tex_scale1).%c%c;\n"
302     "rgba.rgb = yuv_to_rgb (yuv, offset, coeff1, coeff2, coeff3);\n"
303     "rgba.a = 1.0;\n"
304     "gl_FragColor=vec4(rgba.%c,rgba.%c,rgba.%c,rgba.%c);\n";
305
306 static const struct shader_templ templ_SEMI_PLANAR_to_RGB =
307   { NULL,
308     DEFAULT_UNIFORMS YUV_TO_RGB_COEFFICIENTS "uniform sampler2D Ytex, UVtex;\n",
309     { glsl_func_yuv_to_rgb, NULL, },
310     GST_GL_TEXTURE_TARGET_2D
311   };
312
313   /* RGB to NV12/NV21/NV16/NV61 conversion */
314   /* NV12/NV16: u, v
315      NV21/NV61: v, u */
316 static const gchar templ_RGB_to_SEMI_PLANAR_YUV_BODY[] =
317     "vec4 texel, uv_texel;\n"
318     "vec3 yuv;\n"
319     "texel = texture2D(tex, texcoord).%c%c%c%c;\n"
320     "uv_texel = texture2D(tex, texcoord * tex_scale0 * chroma_sampling).%c%c%c%c;\n"
321     "yuv.x = rgb_to_yuv (texel.rgb, offset, coeff1, coeff2, coeff3).x;\n"
322     "yuv.yz = rgb_to_yuv (uv_texel.rgb, offset, coeff1, coeff2, coeff3).yz;\n"
323     "gl_FragData[0] = vec4(yuv.x, 0.0, 0.0, 1.0);\n"
324     "gl_FragData[1] = vec4(yuv.%c, yuv.%c, 0.0, 1.0);\n";
325
326 static const struct shader_templ templ_RGB_to_SEMI_PLANAR_YUV =
327   { NULL,
328     DEFAULT_UNIFORMS RGB_TO_YUV_COEFFICIENTS "uniform sampler2D tex;\n"
329     "uniform vec2 chroma_sampling;\n",
330     { glsl_func_rgb_to_yuv, NULL, },
331     GST_GL_TEXTURE_TARGET_2D
332   };
333
334 /* YUY2:r,g,a
335    UYVY:a,b,r */
336 static const gchar templ_YUY2_UYVY_to_RGB_BODY[] =
337     "vec4 rgba, uv_texel;\n"
338     "vec3 yuv;\n"
339     /* FIXME: should get the sampling right... */
340     "float dx1 = -poffset_x;\n"
341     "float dx2 = 0.0;\n"
342     "yuv.x = texture2D(Ytex, texcoord * tex_scale0).%c;\n"
343     /* v_texcoord are normalized, texcoord may not be e.g. rectangle textures */
344     "float inorder = mod (v_texcoord.x * width, 2.0);\n"
345     "if (inorder < 1.0) {\n"
346     "  dx2 = -dx1;\n"
347     "  dx1 = 0.0;\n"
348     "}\n"
349     "uv_texel.rg = texture2D(Ytex, texcoord * tex_scale0 + vec2(dx1, 0.0)).r%c;\n"
350     "uv_texel.ba = texture2D(Ytex, texcoord * tex_scale0 + vec2(dx2, 0.0)).r%c;\n"
351     "yuv.yz = uv_texel.%c%c;\n"
352     "rgba.rgb = yuv_to_rgb (yuv, offset, coeff1, coeff2, coeff3);\n"
353     "rgba.a = 1.0;\n"
354     "gl_FragColor = vec4(rgba.%c,rgba.%c,rgba.%c,rgba.%c);\n";
355
356 static const struct shader_templ templ_YUY2_UYVY_to_RGB =
357   { NULL,
358     DEFAULT_UNIFORMS YUV_TO_RGB_COEFFICIENTS "uniform sampler2D Ytex;\n",
359     { glsl_func_yuv_to_rgb, NULL, },
360     GST_GL_TEXTURE_TARGET_2D
361   };
362
363 static const gchar templ_RGB_to_YUY2_UYVY_BODY[] =
364     "vec4 texel1, texel2;\n"
365     "vec3 yuv, yuv1, yuv2;\n"
366     "float fx, dx, fy;\n"
367     /* v_texcoord are normalized, texcoord may not be e.g. rectangle textures */
368     "float inorder = mod (v_texcoord.x * width, 2.0);\n"
369     "fx = texcoord.x;\n"
370     "dx = poffset_x;\n"
371     "if (inorder > 1.0) {\n"
372     "  dx = -dx;\n"
373     "}\n"
374     "fy = texcoord.y;\n"
375     "texel1 = texture2D(tex, vec2(fx, fy)).%c%c%c%c;\n"
376     "texel2 = texture2D(tex, vec2(fx + dx, fy)).%c%c%c%c;\n"
377     "yuv1 = rgb_to_yuv (texel1.rgb, offset, coeff1, coeff2, coeff3);\n"
378     "yuv2 = rgb_to_yuv (texel2.rgb, offset, coeff1, coeff2, coeff3);\n"
379     "yuv.x = yuv1.x;\n"
380     "yuv.yz = (yuv1.yz + yuv2.yz) * 0.5;\n"
381     "if (inorder < 1.0) {\n"
382     "  gl_FragColor = vec4(yuv.%c, yuv.%c, 0.0, 0.0);\n"
383     "} else {\n"
384     "  gl_FragColor = vec4(yuv.%c, yuv.%c, 0.0, 0.0);\n"
385     "}\n";
386
387 static const struct shader_templ templ_RGB_to_YUY2_UYVY =
388   { NULL,
389     DEFAULT_UNIFORMS RGB_TO_YUV_COEFFICIENTS "uniform sampler2D tex;\n",
390     { glsl_func_rgb_to_yuv, NULL, },
391     GST_GL_TEXTURE_TARGET_2D
392   };
393
394 static const gchar text_vertex_shader[] =
395     "attribute vec4 a_position;   \n"
396     "attribute vec2 a_texcoord;   \n"
397     "varying vec2 v_texcoord;     \n"
398     "void main()                  \n"
399     "{                            \n"
400     "  gl_Position = a_position; \n"
401     "  v_texcoord = a_texcoord;  \n"
402     "}                            \n";
403
404 static const GLfloat vertices[] = {
405      1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
406     -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
407     -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,
408      1.0f,  1.0f, 0.0f, 1.0f, 1.0f
409 };
410
411 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
412
413 /* *INDENT-ON* */
414
415 struct ConvertInfo
416 {
417   gint in_n_textures;
418   gint out_n_textures;
419   const struct shader_templ *templ;
420   gchar *frag_body;
421   gchar *frag_prog;
422   const gchar *shader_tex_names[GST_VIDEO_MAX_PLANES];
423   gfloat *cms_offset;
424   gfloat *cms_coeff1;           /* r,y */
425   gfloat *cms_coeff2;           /* g,u */
426   gfloat *cms_coeff3;           /* b,v */
427   gfloat chroma_sampling[2];
428 };
429
430 struct _GstGLColorConvertPrivate
431 {
432   gboolean result;
433
434   struct ConvertInfo convert_info;
435
436   GstGLTextureTarget from_texture_target;
437   GstGLTextureTarget to_texture_target;
438
439   GstGLMemory *in_tex[GST_VIDEO_MAX_PLANES];
440   GstGLMemory *out_tex[GST_VIDEO_MAX_PLANES];
441
442   GstGLFormat in_tex_formats[GST_VIDEO_MAX_PLANES];
443
444   GLuint vao;
445   GLuint vertex_buffer;
446   GLuint vbo_indices;
447   GLuint attr_position;
448   GLuint attr_texture;
449
450   GstCaps *in_caps;
451   GstCaps *out_caps;
452
453   GstBufferPool *pool;
454   gboolean pool_started;
455 };
456
457 GST_DEBUG_CATEGORY_STATIC (gst_gl_color_convert_debug);
458 #define GST_CAT_DEFAULT gst_gl_color_convert_debug
459
460 #define DEBUG_INIT \
461   GST_DEBUG_CATEGORY_INIT (gst_gl_color_convert_debug, "glconvert", 0, "convert");
462
463 G_DEFINE_TYPE_WITH_CODE (GstGLColorConvert, gst_gl_color_convert,
464     GST_TYPE_OBJECT, G_ADD_PRIVATE (GstGLColorConvert) DEBUG_INIT);
465
466 static void gst_gl_color_convert_finalize (GObject * object);
467 static void gst_gl_color_convert_reset (GstGLColorConvert * convert);
468
469 #define GST_GL_COLOR_CONVERT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
470     GST_TYPE_GL_COLOR_CONVERT, GstGLColorConvertPrivate))
471
472 static void
473 gst_gl_color_convert_class_init (GstGLColorConvertClass * klass)
474 {
475   G_OBJECT_CLASS (klass)->finalize = gst_gl_color_convert_finalize;
476 }
477
478 static void
479 gst_gl_color_convert_init (GstGLColorConvert * convert)
480 {
481   convert->priv = gst_gl_color_convert_get_instance_private (convert);
482
483   gst_gl_color_convert_reset (convert);
484 }
485
486 /**
487  * gst_gl_color_convert_new:
488  * @context: a #GstGLContext
489  *
490  * Returns: (transfer full): a new #GstGLColorConvert object
491  *
492  * Since: 1.4
493  */
494 GstGLColorConvert *
495 gst_gl_color_convert_new (GstGLContext * context)
496 {
497   GstGLColorConvert *convert;
498
499   convert = g_object_new (GST_TYPE_GL_COLOR_CONVERT, NULL);
500   gst_object_ref_sink (convert);
501
502   convert->context = gst_object_ref (context);
503
504   gst_video_info_set_format (&convert->in_info, GST_VIDEO_FORMAT_ENCODED, 0, 0);
505   gst_video_info_set_format (&convert->out_info, GST_VIDEO_FORMAT_ENCODED, 0,
506       0);
507
508   GST_DEBUG_OBJECT (convert,
509       "Created new colorconvert for context %" GST_PTR_FORMAT, context);
510
511   return convert;
512 }
513
514 static void
515 gst_gl_color_convert_finalize (GObject * object)
516 {
517   GstGLColorConvert *convert;
518
519   convert = GST_GL_COLOR_CONVERT (object);
520
521   gst_gl_color_convert_reset (convert);
522
523   if (convert->context) {
524     gst_object_unref (convert->context);
525     convert->context = NULL;
526   }
527
528   G_OBJECT_CLASS (gst_gl_color_convert_parent_class)->finalize (object);
529 }
530
531 static void
532 _reset_gl (GstGLContext * context, GstGLColorConvert * convert)
533 {
534   const GstGLFuncs *gl = context->gl_vtable;
535
536   if (convert->priv->vao) {
537     gl->DeleteVertexArrays (1, &convert->priv->vao);
538     convert->priv->vao = 0;
539   }
540
541   if (convert->priv->vertex_buffer) {
542     gl->DeleteBuffers (1, &convert->priv->vertex_buffer);
543     convert->priv->vertex_buffer = 0;
544   }
545
546   if (convert->priv->vbo_indices) {
547     gl->DeleteBuffers (1, &convert->priv->vbo_indices);
548     convert->priv->vbo_indices = 0;
549   }
550 }
551
552 static void
553 gst_gl_color_convert_reset_shader (GstGLColorConvert * convert)
554 {
555   convert->priv->convert_info.chroma_sampling[0] = 1.0f;
556   convert->priv->convert_info.chroma_sampling[1] = 1.0f;
557
558   if (convert->priv->convert_info.frag_prog) {
559     g_free (convert->priv->convert_info.frag_prog);
560     convert->priv->convert_info.frag_prog = NULL;
561   }
562   if (convert->priv->convert_info.frag_body) {
563     g_free (convert->priv->convert_info.frag_body);
564     convert->priv->convert_info.frag_body = NULL;
565   }
566   if (convert->shader) {
567     gst_object_unref (convert->shader);
568     convert->shader = NULL;
569   }
570
571   convert->initted = FALSE;
572 }
573
574 static void
575 gst_gl_color_convert_reset (GstGLColorConvert * convert)
576 {
577   guint i;
578
579   if (convert->fbo) {
580     gst_object_unref (convert->fbo);
581     convert->fbo = NULL;
582   }
583
584   for (i = 0; i < convert->priv->convert_info.out_n_textures; i++) {
585     if (convert->priv->out_tex[i])
586       gst_memory_unref ((GstMemory *) convert->priv->out_tex[i]);
587     convert->priv->out_tex[i] = NULL;
588   }
589
590   if (convert->priv->pool) {
591     convert->priv->pool_started = FALSE;
592
593     gst_object_unref (convert->priv->pool);
594     convert->priv->pool = NULL;
595   }
596
597   gst_caps_replace (&convert->priv->in_caps, NULL);
598   gst_caps_replace (&convert->priv->out_caps, NULL);
599
600   if (convert->context) {
601     gst_gl_context_thread_add (convert->context,
602         (GstGLContextThreadFunc) _reset_gl, convert);
603   }
604
605   gst_gl_color_convert_reset_shader (convert);
606 }
607
608 static gboolean
609 _gst_gl_color_convert_can_passthrough_info (GstVideoInfo * in,
610     GstVideoInfo * out)
611 {
612   gint i;
613
614   if (GST_VIDEO_INFO_FORMAT (in) != GST_VIDEO_INFO_FORMAT (out))
615     return FALSE;
616   if (GST_VIDEO_INFO_WIDTH (in) != GST_VIDEO_INFO_WIDTH (out))
617     return FALSE;
618   if (GST_VIDEO_INFO_HEIGHT (in) != GST_VIDEO_INFO_HEIGHT (out))
619     return FALSE;
620   if (GST_VIDEO_INFO_SIZE (in) != GST_VIDEO_INFO_SIZE (out))
621     return FALSE;
622
623   for (i = 0; i < in->finfo->n_planes; i++) {
624     if (in->stride[i] != out->stride[i])
625       return FALSE;
626     if (in->offset[i] != out->offset[i])
627       return FALSE;
628   }
629
630   if (!gst_video_colorimetry_is_equal (&in->colorimetry, &out->colorimetry))
631     return FALSE;
632   if (in->chroma_site != out->chroma_site)
633     return FALSE;
634
635   return TRUE;
636 }
637
638 static gboolean
639 _gst_gl_color_convert_set_caps_unlocked (GstGLColorConvert * convert,
640     GstCaps * in_caps, GstCaps * out_caps)
641 {
642   GstVideoInfo in_info, out_info;
643   GstCapsFeatures *in_features, *out_features;
644   GstGLTextureTarget from_target, to_target;
645   gboolean passthrough;
646
647   g_return_val_if_fail (convert != NULL, FALSE);
648   g_return_val_if_fail (in_caps, FALSE);
649   g_return_val_if_fail (out_caps, FALSE);
650
651   GST_LOG_OBJECT (convert, "Setting caps in %" GST_PTR_FORMAT
652       " out %" GST_PTR_FORMAT, in_caps, out_caps);
653
654   if (!gst_video_info_from_caps (&in_info, in_caps))
655     g_assert_not_reached ();
656
657   if (!gst_video_info_from_caps (&out_info, out_caps))
658     g_assert_not_reached ();
659
660   g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&in_info) !=
661       GST_VIDEO_FORMAT_UNKNOWN, FALSE);
662   g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&in_info) !=
663       GST_VIDEO_FORMAT_ENCODED, FALSE);
664   g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&out_info) !=
665       GST_VIDEO_FORMAT_UNKNOWN, FALSE);
666   g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&out_info) !=
667       GST_VIDEO_FORMAT_ENCODED, FALSE);
668
669   in_features = gst_caps_get_features (in_caps, 0);
670   out_features = gst_caps_get_features (out_caps, 0);
671
672   if (!gst_caps_features_contains (in_features,
673           GST_CAPS_FEATURE_MEMORY_GL_MEMORY)
674       || !gst_caps_features_contains (out_features,
675           GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
676     return FALSE;
677   }
678
679   {
680     GstStructure *in_s = gst_caps_get_structure (in_caps, 0);
681     GstStructure *out_s = gst_caps_get_structure (out_caps, 0);
682
683     if (gst_structure_has_field_typed (in_s, "texture-target", G_TYPE_STRING))
684       from_target =
685           gst_gl_texture_target_from_string (gst_structure_get_string (in_s,
686               "texture-target"));
687     else
688       from_target = GST_GL_TEXTURE_TARGET_2D;
689
690     if (gst_structure_has_field_typed (out_s, "texture-target", G_TYPE_STRING))
691       to_target =
692           gst_gl_texture_target_from_string (gst_structure_get_string (out_s,
693               "texture-target"));
694     else
695       to_target = GST_GL_TEXTURE_TARGET_2D;
696
697     if (to_target == GST_GL_TEXTURE_TARGET_NONE
698         || from_target == GST_GL_TEXTURE_TARGET_NONE)
699       /* invalid caps */
700       return FALSE;
701   }
702
703   if (gst_video_info_is_equal (&convert->in_info, &in_info) &&
704       gst_video_info_is_equal (&convert->out_info, &out_info) &&
705       convert->priv->from_texture_target == from_target &&
706       convert->priv->to_texture_target == to_target)
707     return TRUE;
708
709   /* If input and output are identical, pass through directly */
710   passthrough =
711       _gst_gl_color_convert_can_passthrough_info (&in_info, &out_info) &&
712       from_target == to_target;
713
714   if (!passthrough && to_target != GST_GL_TEXTURE_TARGET_2D
715       && to_target != GST_GL_TEXTURE_TARGET_RECTANGLE)
716     return FALSE;
717
718   {
719     guint yuv_gray_flags, in_flags, out_flags;
720
721     in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info.finfo);
722     out_flags = GST_VIDEO_FORMAT_INFO_FLAGS (out_info.finfo);
723     yuv_gray_flags = GST_VIDEO_FORMAT_FLAG_YUV | GST_VIDEO_FORMAT_FLAG_GRAY;
724
725     /* GRAY/YUV -> GRAY/YUV is not supported for non-passthrough */
726     if (!passthrough && (in_flags & yuv_gray_flags) != 0
727         && (out_flags & yuv_gray_flags) != 0)
728       return FALSE;
729   }
730
731   gst_gl_color_convert_reset (convert);
732   convert->in_info = in_info;
733   convert->out_info = out_info;
734   gst_caps_replace (&convert->priv->in_caps, in_caps);
735   gst_caps_replace (&convert->priv->out_caps, out_caps);
736   convert->priv->from_texture_target = from_target;
737   convert->priv->to_texture_target = to_target;
738   convert->initted = FALSE;
739
740   convert->passthrough = passthrough;
741 #ifndef GST_DISABLE_GST_DEBUG
742   if (G_UNLIKELY (convert->passthrough))
743     GST_DEBUG_OBJECT (convert,
744         "Configuring passthrough mode for same in/out caps");
745   else {
746     GST_DEBUG_OBJECT (convert, "Color converting %" GST_PTR_FORMAT
747         " to %" GST_PTR_FORMAT, in_caps, out_caps);
748   }
749 #endif
750
751   return TRUE;
752 }
753
754 /**
755  * gst_gl_color_convert_set_caps:
756  * @convert: a #GstGLColorConvert
757  * @in_caps: input #GstCaps
758  * @out_caps: output #GstCaps
759  *
760  * Initializes @convert with the information required for conversion.
761  *
762  * Since: 1.6
763  */
764 gboolean
765 gst_gl_color_convert_set_caps (GstGLColorConvert * convert,
766     GstCaps * in_caps, GstCaps * out_caps)
767 {
768   gboolean ret;
769
770   GST_OBJECT_LOCK (convert);
771   ret = _gst_gl_color_convert_set_caps_unlocked (convert, in_caps, out_caps);
772   GST_OBJECT_UNLOCK (convert);
773
774   return ret;
775 }
776
777 /**
778  * gst_gl_color_convert_decide_allocation:
779  * @convert: a #GstGLColorConvert
780  * @query: a completed ALLOCATION #GstQuery
781  *
782  * Provides an implementation of #GstBaseTransformClass.decide_allocation()
783  *
784  * Returns: whether the allocation parameters were successfully chosen
785  *
786  * Since: 1.8
787  */
788 gboolean
789 gst_gl_color_convert_decide_allocation (GstGLColorConvert * convert,
790     GstQuery * query)
791 {
792   GstBufferPool *pool = NULL;
793   GstStructure *config;
794   GstCaps *caps;
795   guint min, max, size, n, i;
796   gboolean update_pool;
797   GstGLVideoAllocationParams *params;
798   GstVideoInfo vinfo;
799
800   gst_query_parse_allocation (query, &caps, NULL);
801   if (!caps)
802     return FALSE;
803
804   gst_video_info_from_caps (&vinfo, caps);
805
806   n = gst_query_get_n_allocation_pools (query);
807   if (n > 0) {
808     update_pool = TRUE;
809     for (i = 0; i < n; i++) {
810       gst_query_parse_nth_allocation_pool (query, i, &pool, &size, &min, &max);
811
812       if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) {
813         if (pool)
814           gst_object_unref (pool);
815         pool = NULL;
816       }
817     }
818   }
819
820   if (!pool) {
821     GstVideoInfo vinfo;
822
823     gst_video_info_init (&vinfo);
824     size = vinfo.size;
825     min = max = 0;
826     update_pool = FALSE;
827   }
828
829   if (!pool)
830     pool = gst_gl_buffer_pool_new (convert->context);
831
832   config = gst_buffer_pool_get_config (pool);
833
834   gst_buffer_pool_config_set_params (config, caps, size, min, max);
835   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
836   if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL))
837     gst_buffer_pool_config_add_option (config,
838         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
839
840   params = gst_gl_video_allocation_params_new (convert->context, NULL, &vinfo,
841       0, NULL, convert->priv->to_texture_target, 0);
842   gst_buffer_pool_config_set_gl_allocation_params (config,
843       (GstGLAllocationParams *) params);
844   gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
845
846   if (!gst_buffer_pool_set_config (pool, config))
847     GST_WARNING_OBJECT (convert, "Failed to set buffer pool config");
848
849   if (update_pool)
850     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
851   else
852     gst_query_add_allocation_pool (query, pool, size, min, max);
853
854   if (convert->priv->pool) {
855     gst_object_unref (convert->priv->pool);
856     convert->priv->pool_started = FALSE;
857   }
858   convert->priv->pool = pool;
859
860   return TRUE;
861 }
862
863 static void
864 _init_value_string_list (GValue * list, ...)
865 {
866   GValue item = G_VALUE_INIT;
867   gchar *str;
868   va_list args;
869
870   g_value_init (list, GST_TYPE_LIST);
871
872   va_start (args, list);
873   while ((str = va_arg (args, gchar *))) {
874     g_value_init (&item, G_TYPE_STRING);
875     g_value_set_string (&item, str);
876
877     gst_value_list_append_value (list, &item);
878     g_value_unset (&item);
879   }
880   va_end (args);
881 }
882
883 static void
884 _append_value_string_list (GValue * list, ...)
885 {
886   GValue item = G_VALUE_INIT;
887   gchar *str;
888   va_list args;
889
890   va_start (args, list);
891   while ((str = va_arg (args, gchar *))) {
892     g_value_init (&item, G_TYPE_STRING);
893     g_value_set_string (&item, str);
894
895     gst_value_list_append_value (list, &item);
896     g_value_unset (&item);
897   }
898   va_end (args);
899 }
900
901 static void
902 _init_supported_formats (GstGLContext * context, gboolean output,
903     GValue * supported_formats)
904 {
905   /* Assume if context == NULL that we don't have a GL context and can
906    * do the conversion */
907
908   /* Always supported input and output formats */
909   _init_value_string_list (supported_formats, "RGBA", "RGB", "RGBx", "BGR",
910       "BGRx", "BGRA", "xRGB", "xBGR", "ARGB", "ABGR", "GRAY8", "GRAY16_LE",
911       "GRAY16_BE", "AYUV", "VUYA", "YUY2", "UYVY", NULL);
912
913   /* Always supported input formats or output with multiple draw buffers */
914   if (!output || (!context || context->gl_vtable->DrawBuffers))
915     _append_value_string_list (supported_formats, "Y444", "I420", "YV12",
916         "Y42B", "Y41B", "NV12", "NV21", "NV16", "NV61", NULL);
917
918   /* Requires reading from a RG/LA framebuffer... */
919   if (!context || (USING_GLES3 (context) || USING_OPENGL (context)))
920     _append_value_string_list (supported_formats, "YUY2", "UYVY", NULL);
921
922   if (!context || gst_gl_format_is_supported (context, GST_GL_RGBA16))
923     _append_value_string_list (supported_formats, "ARGB64", NULL);
924
925   if (!context || gst_gl_format_is_supported (context, GST_GL_RGB565))
926     _append_value_string_list (supported_formats, "RGB16", "BGR16", NULL);
927
928   if (!context || gst_gl_format_is_supported (context, GST_GL_RGB10_A2)) {
929 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
930     _append_value_string_list (supported_formats, "BGR10A2_LE", "RGB10A2_LE",
931         "Y410", NULL);
932 #else
933     _append_value_string_list (supported_formats, "Y410", NULL);
934 #endif
935   }
936
937   if (!context || (gst_gl_format_is_supported (context, GST_GL_R16) &&
938           gst_gl_format_is_supported (context, GST_GL_RG16)))
939 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
940     _append_value_string_list (supported_formats, "P010_10LE", "P016_LE", NULL);
941 #else
942     _append_value_string_list (supported_formats, "P010_10BE", "P016_BE", NULL);
943 #endif
944 }
945
946 /* copies the given caps */
947 static GstCaps *
948 gst_gl_color_convert_caps_transform_format_info (GstGLContext * context,
949     gboolean output, GstCaps * caps)
950 {
951   GstStructure *st;
952   GstCapsFeatures *f;
953   gint i, n;
954   GstCaps *res;
955   GValue supported_formats = G_VALUE_INIT;
956   GValue rgb_formats = G_VALUE_INIT;
957   GValue supported_rgb_formats = G_VALUE_INIT;
958
959   /* There are effectively two modes here with the RGB/YUV transition:
960    * 1. There is a RGB-like format as input and we can transform to YUV or,
961    * 2. No RGB-like format as input so we can only transform to RGB-like formats
962    *
963    * We also filter down the list of formats depending on what the OpenGL
964    * context supports (when provided).
965    */
966
967   _init_value_string_list (&rgb_formats, "RGBA", "ARGB", "BGRA", "ABGR", "RGBx",
968       "xRGB", "BGRx", "xBGR", "RGB", "BGR", "ARGB64", "BGR10A2_LE",
969       "RGB10A2_LE", NULL);
970   _init_supported_formats (context, output, &supported_formats);
971   gst_value_intersect (&supported_rgb_formats, &rgb_formats,
972       &supported_formats);
973
974   res = gst_caps_new_empty ();
975
976   n = gst_caps_get_size (caps);
977   for (i = 0; i < n; i++) {
978     const GValue *format;
979
980     st = gst_caps_get_structure (caps, i);
981     f = gst_caps_get_features (caps, i);
982
983     format = gst_structure_get_value (st, "format");
984     st = gst_structure_copy (st);
985     if (GST_VALUE_HOLDS_LIST (format)) {
986       gboolean have_rgb_formats = FALSE;
987       GValue passthrough_formats = G_VALUE_INIT;
988       gint j, len;
989
990       g_value_init (&passthrough_formats, GST_TYPE_LIST);
991       len = gst_value_list_get_size (format);
992       for (j = 0; j < len; j++) {
993         const GValue *val;
994
995         val = gst_value_list_get_value (format, j);
996         if (G_VALUE_HOLDS_STRING (val)) {
997           const gchar *format_str = g_value_get_string (val);
998           GstVideoFormat v_format = gst_video_format_from_string (format_str);
999           const GstVideoFormatInfo *t_info =
1000               gst_video_format_get_info (v_format);
1001           if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) & (GST_VIDEO_FORMAT_FLAG_YUV
1002                   | GST_VIDEO_FORMAT_FLAG_GRAY)) {
1003             gst_value_list_append_value (&passthrough_formats, val);
1004           } else if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) &
1005               GST_VIDEO_FORMAT_FLAG_RGB) {
1006             have_rgb_formats = TRUE;
1007             break;
1008           }
1009         }
1010       }
1011       if (have_rgb_formats) {
1012         gst_structure_set_value (st, "format", &supported_formats);
1013       } else {
1014         /* add passthrough structure, then the rgb conversion structure */
1015         gst_structure_set_value (st, "format", &passthrough_formats);
1016         gst_caps_append_structure_full (res, gst_structure_copy (st),
1017             gst_caps_features_copy (f));
1018         gst_structure_set_value (st, "format", &supported_rgb_formats);
1019       }
1020       g_value_unset (&passthrough_formats);
1021     } else if (G_VALUE_HOLDS_STRING (format)) {
1022       const gchar *format_str = g_value_get_string (format);
1023       GstVideoFormat v_format = gst_video_format_from_string (format_str);
1024       const GstVideoFormatInfo *t_info = gst_video_format_get_info (v_format);
1025       if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) & (GST_VIDEO_FORMAT_FLAG_YUV |
1026               GST_VIDEO_FORMAT_FLAG_GRAY)) {
1027         /* add passthrough structure, then the rgb conversion structure */
1028         gst_structure_set_value (st, "format", format);
1029         gst_caps_append_structure_full (res, gst_structure_copy (st),
1030             gst_caps_features_copy (f));
1031         gst_structure_set_value (st, "format", &supported_rgb_formats);
1032       } else {                  /* RGB */
1033         gst_structure_set_value (st, "format", &supported_formats);
1034       }
1035     }
1036     gst_structure_remove_fields (st, "colorimetry", "chroma-site",
1037         "texture-target", NULL);
1038
1039     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
1040   }
1041
1042   g_value_unset (&supported_formats);
1043   g_value_unset (&rgb_formats);
1044   g_value_unset (&supported_rgb_formats);
1045
1046   return res;
1047 }
1048
1049 /**
1050  * gst_gl_color_convert_transform_caps:
1051  * @context: a #GstGLContext to use for transforming @caps
1052  * @direction: a #GstPadDirection
1053  * @caps: (transfer none): the #GstCaps to transform
1054  * @filter: (transfer none): a set of filter #GstCaps
1055  *
1056  * Provides an implementation of #GstBaseTransformClass.transform_caps()
1057  *
1058  * Returns: (transfer full): the converted #GstCaps
1059  *
1060  * Since: 1.6
1061  */
1062 GstCaps *
1063 gst_gl_color_convert_transform_caps (GstGLContext * context,
1064     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1065 {
1066   caps = gst_gl_color_convert_caps_transform_format_info (context,
1067       direction == GST_PAD_SRC, caps);
1068
1069   if (filter) {
1070     GstCaps *tmp;
1071
1072     tmp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1073     gst_caps_unref (caps);
1074     caps = tmp;
1075   }
1076
1077   return caps;
1078 }
1079
1080 /* fixation from videoconvert */
1081 #define SCORE_FORMAT_CHANGE             1
1082 #define SCORE_DEPTH_CHANGE              1
1083 #define SCORE_ALPHA_CHANGE              1
1084 #define SCORE_CHROMA_W_CHANGE           1
1085 #define SCORE_CHROMA_H_CHANGE           1
1086 #define SCORE_PALETTE_CHANGE            1
1087
1088 #define SCORE_COLORSPACE_LOSS           2       /* RGB <-> YUV */
1089 #define SCORE_DEPTH_LOSS                4       /* change bit depth */
1090 #define SCORE_ALPHA_LOSS                8       /* lose the alpha channel */
1091 #define SCORE_CHROMA_W_LOSS             16      /* vertical subsample */
1092 #define SCORE_CHROMA_H_LOSS             32      /* horizontal subsample */
1093 #define SCORE_PALETTE_LOSS              64      /* convert to palette format */
1094 #define SCORE_COLOR_LOSS                128     /* convert to GRAY */
1095
1096 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
1097                          GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
1098 #define ALPHA_MASK      (GST_VIDEO_FORMAT_FLAG_ALPHA)
1099 #define PALETTE_MASK    (GST_VIDEO_FORMAT_FLAG_PALETTE)
1100
1101 static GstGLTextureTarget
1102 _texture_target_demask (guint target_mask)
1103 {
1104   if (target_mask & (1 << GST_GL_TEXTURE_TARGET_2D)) {
1105     return GST_GL_TEXTURE_TARGET_2D;
1106   }
1107   if (target_mask & (1 << GST_GL_TEXTURE_TARGET_RECTANGLE)) {
1108     return GST_GL_TEXTURE_TARGET_RECTANGLE;
1109   }
1110   if (target_mask & (1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES)) {
1111     return GST_GL_TEXTURE_TARGET_EXTERNAL_OES;
1112   }
1113
1114   return 0;
1115 }
1116
1117 /* calculate how much loss a conversion would be */
1118 static void
1119 score_format_target (const GstVideoFormatInfo * in_info, guint targets_mask,
1120     GstVideoFormat v_format, guint other_targets_mask, gint * min_loss,
1121     const GstVideoFormatInfo ** out_info, GstGLTextureTarget * result)
1122 {
1123   const GstVideoFormatInfo *t_info;
1124   GstVideoFormatFlags in_flags, t_flags;
1125   gint loss;
1126
1127   t_info = gst_video_format_get_info (v_format);
1128   if (!t_info)
1129     return;
1130
1131   /* accept input format immediately without loss */
1132   if (in_info == t_info && (targets_mask & other_targets_mask) != 0) {
1133     *min_loss = 0;
1134     *out_info = t_info;
1135     *result = _texture_target_demask (targets_mask & other_targets_mask);
1136     return;
1137   }
1138
1139   /* can only passthrough external-oes textures */
1140   other_targets_mask &= ~(1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES);
1141   if (other_targets_mask == 0)
1142     return;
1143   /* try to keep the same target */
1144   if (targets_mask & other_targets_mask)
1145     other_targets_mask = targets_mask & other_targets_mask;
1146
1147   loss = SCORE_FORMAT_CHANGE;
1148
1149   in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
1150   in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1151   in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1152   in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1153
1154   t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
1155   t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1156   t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1157   t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1158
1159   /* GRAY/YUV -> GRAY/YUV is not supported */
1160   if ((in_flags & (GST_VIDEO_FORMAT_FLAG_YUV | GST_VIDEO_FORMAT_FLAG_GRAY)) != 0
1161       && (t_flags & (GST_VIDEO_FORMAT_FLAG_YUV | GST_VIDEO_FORMAT_FLAG_GRAY)) !=
1162       0)
1163     return;
1164
1165   if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
1166     loss += SCORE_PALETTE_CHANGE;
1167     if (t_flags & PALETTE_MASK)
1168       loss += SCORE_PALETTE_LOSS;
1169   }
1170
1171   if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
1172     loss += SCORE_COLORSPACE_LOSS;
1173     if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
1174       loss += SCORE_COLOR_LOSS;
1175   }
1176
1177   if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
1178     loss += SCORE_ALPHA_CHANGE;
1179     if (in_flags & ALPHA_MASK)
1180       loss += SCORE_ALPHA_LOSS;
1181   }
1182
1183   if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
1184     loss += SCORE_CHROMA_H_CHANGE;
1185     if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
1186       loss += SCORE_CHROMA_H_LOSS;
1187   }
1188   if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
1189     loss += SCORE_CHROMA_W_CHANGE;
1190     if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
1191       loss += SCORE_CHROMA_W_LOSS;
1192   }
1193
1194   if ((in_info->bits) != (t_info->bits)) {
1195     loss += SCORE_DEPTH_CHANGE;
1196     if ((in_info->bits) > (t_info->bits))
1197       loss += SCORE_DEPTH_LOSS;
1198   }
1199
1200   if (loss < *min_loss) {
1201     GstGLTextureTarget target = _texture_target_demask (other_targets_mask);
1202
1203     if (target != 0) {
1204       *out_info = t_info;
1205       *min_loss = loss;
1206       *result = target;
1207     }
1208   }
1209 }
1210
1211 static void
1212 gst_gl_color_convert_fixate_format_target (GstCaps * caps, GstCaps * result)
1213 {
1214   GstStructure *ins, *outs;
1215   const gchar *in_format;
1216   const GstVideoFormatInfo *in_info, *out_info = NULL;
1217   const GValue *targets;
1218   guint targets_mask = 0;
1219   GstGLTextureTarget target;
1220   gint min_loss = G_MAXINT;
1221   guint i, capslen;
1222
1223   ins = gst_caps_get_structure (caps, 0);
1224   in_format = gst_structure_get_string (ins, "format");
1225   if (!in_format)
1226     return;
1227   targets = gst_structure_get_value (ins, "texture-target");
1228   targets_mask = gst_gl_value_get_texture_target_mask (targets);
1229   if (!targets_mask)
1230     return;
1231
1232   in_info =
1233       gst_video_format_get_info (gst_video_format_from_string (in_format));
1234   if (!in_info)
1235     return;
1236
1237   outs = gst_caps_get_structure (result, 0);
1238
1239   capslen = gst_caps_get_size (result);
1240   for (i = 0; i < capslen; i++) {
1241     GstStructure *tests;
1242     const GValue *format;
1243     const GValue *other_targets;
1244     guint other_targets_mask = 0;
1245
1246     tests = gst_caps_get_structure (result, i);
1247
1248     format = gst_structure_get_value (tests, "format");
1249     other_targets = gst_structure_get_value (tests, "texture-target");
1250     /* should not happen */
1251     if (format == NULL || other_targets == NULL)
1252       continue;
1253
1254     other_targets_mask = gst_gl_value_get_texture_target_mask (other_targets);
1255
1256     if (GST_VALUE_HOLDS_LIST (format)) {
1257       gint j, len;
1258
1259       len = gst_value_list_get_size (format);
1260       for (j = 0; j < len; j++) {
1261         const GValue *val;
1262
1263         val = gst_value_list_get_value (format, j);
1264         if (G_VALUE_HOLDS_STRING (val)) {
1265           const gchar *format_str = g_value_get_string (val);
1266           GstVideoFormat v_format = gst_video_format_from_string (format_str);
1267           score_format_target (in_info, targets_mask, v_format,
1268               other_targets_mask, &min_loss, &out_info, &target);
1269           if (min_loss == 0)
1270             break;
1271         }
1272       }
1273     } else if (G_VALUE_HOLDS_STRING (format)) {
1274       const gchar *format_str = g_value_get_string (format);
1275       GstVideoFormat v_format = gst_video_format_from_string (format_str);
1276       score_format_target (in_info, targets_mask, v_format, other_targets_mask,
1277           &min_loss, &out_info, &target);
1278     }
1279   }
1280   if (out_info)
1281     gst_structure_set (outs, "format", G_TYPE_STRING,
1282         GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
1283   if (target)
1284     gst_structure_set (outs, "texture-target", G_TYPE_STRING,
1285         gst_gl_texture_target_to_string (target), NULL);
1286 }
1287
1288 /**
1289  * gst_gl_color_convert_fixate_caps:
1290  * @context: a #GstGLContext to use for transforming @caps
1291  * @direction: a #GstPadDirection
1292  * @caps: (transfer none): the #GstCaps of @direction
1293  * @other: (transfer full): the #GstCaps to fixate
1294  *
1295  * Provides an implementation of #GstBaseTransformClass.fixate_caps()
1296  *
1297  * Returns: (transfer full): the fixated #GstCaps
1298  *
1299  * Since: 1.8
1300  */
1301 GstCaps *
1302 gst_gl_color_convert_fixate_caps (GstGLContext * context,
1303     GstPadDirection direction, GstCaps * caps, GstCaps * other)
1304 {
1305   GstCaps *result;
1306
1307   result = gst_caps_intersect (other, caps);
1308   if (gst_caps_is_empty (result)) {
1309     gst_caps_unref (result);
1310     result = other;
1311   } else {
1312     gst_caps_unref (other);
1313   }
1314
1315   result = gst_caps_make_writable (result);
1316   gst_gl_color_convert_fixate_format_target (caps, result);
1317
1318   result = gst_caps_fixate (result);
1319
1320   if (direction == GST_PAD_SINK) {
1321     if (gst_caps_is_subset (caps, result)) {
1322       gst_caps_replace (&result, caps);
1323     }
1324   }
1325
1326   return result;
1327 }
1328
1329 /**
1330  * gst_gl_color_convert_perform:
1331  * @convert: a #GstGLColorConvert
1332  * @inbuf: (transfer none): the #GstGLMemory filled #GstBuffer to convert
1333  *
1334  * Converts the data contained by @inbuf using the formats specified by the
1335  * #GstCaps passed to gst_gl_color_convert_set_caps()
1336  *
1337  * Returns: (transfer full): a converted #GstBuffer or %NULL
1338  *
1339  * Since: 1.4
1340  */
1341 GstBuffer *
1342 gst_gl_color_convert_perform (GstGLColorConvert * convert, GstBuffer * inbuf)
1343 {
1344   GstBuffer *ret;
1345
1346   g_return_val_if_fail (convert != NULL, FALSE);
1347
1348   GST_OBJECT_LOCK (convert);
1349   ret = _gst_gl_color_convert_perform_unlocked (convert, inbuf);
1350   GST_OBJECT_UNLOCK (convert);
1351
1352   return ret;
1353 }
1354
1355 static GstBuffer *
1356 _gst_gl_color_convert_perform_unlocked (GstGLColorConvert * convert,
1357     GstBuffer * inbuf)
1358 {
1359   g_return_val_if_fail (convert != NULL, FALSE);
1360   g_return_val_if_fail (inbuf, FALSE);
1361
1362   if (G_UNLIKELY (convert->passthrough))
1363     return gst_buffer_ref (inbuf);
1364
1365   convert->inbuf = inbuf;
1366
1367   gst_gl_context_thread_add (convert->context,
1368       (GstGLContextThreadFunc) _do_convert, convert);
1369
1370   if (!convert->priv->result) {
1371     if (convert->outbuf)
1372       gst_buffer_unref (convert->outbuf);
1373     convert->outbuf = NULL;
1374     return NULL;
1375   }
1376
1377   return convert->outbuf;
1378 }
1379
1380 static inline gboolean
1381 _is_RGBx (GstVideoFormat v_format)
1382 {
1383   switch (v_format) {
1384     case GST_VIDEO_FORMAT_RGBx:
1385     case GST_VIDEO_FORMAT_xRGB:
1386     case GST_VIDEO_FORMAT_BGRx:
1387     case GST_VIDEO_FORMAT_xBGR:
1388       return TRUE;
1389     default:
1390       return FALSE;
1391   }
1392 }
1393
1394 static inline gchar
1395 _index_to_shader_swizzle (int idx)
1396 {
1397   switch (idx) {
1398     case 0:
1399       return 'r';
1400     case 1:
1401       return 'g';
1402     case 2:
1403       return 'b';
1404     case 3:
1405       return 'a';
1406     default:
1407       return '#';
1408   }
1409 }
1410
1411 /* attempts to transform expected to want using swizzling */
1412 static gchar *
1413 _RGB_pixel_order (const gchar * expected, const gchar * wanted)
1414 {
1415   GString *ret = g_string_sized_new (4);
1416   gchar *expect, *want;
1417   gchar *orig_want;
1418   int len;
1419   gboolean discard_output = TRUE;
1420
1421   if (g_ascii_strcasecmp (expected, wanted) == 0) {
1422     g_string_free (ret, TRUE);
1423     return g_ascii_strdown (expected, -1);
1424   }
1425
1426   expect = g_ascii_strdown (expected, -1);
1427   orig_want = want = g_ascii_strdown (wanted, -1);
1428
1429   if (strcmp (expect, "rgb16") == 0 || strcmp (expect, "bgr16") == 0) {
1430     gchar *temp = expect;
1431     expect = g_strndup (temp, 3);
1432     g_free (temp);
1433   } else if (strcmp (expect, "bgr10a2_le") == 0) {
1434     gchar *temp = expect;
1435     expect = g_strndup ("bgra", 4);
1436     g_free (temp);
1437   } else if (strcmp (expect, "rgb10a2_le") == 0) {
1438     gchar *temp = expect;
1439     expect = g_strndup ("rgba", 4);
1440     g_free (temp);
1441   }
1442
1443   if (strcmp (want, "rgb16") == 0 || strcmp (want, "bgr16") == 0) {
1444     gchar *temp = want;
1445     orig_want = want = g_strndup (temp, 3);
1446     g_free (temp);
1447   } else if (strcmp (want, "bgr10a2_le") == 0) {
1448     gchar *temp = want;
1449     orig_want = want = g_strndup ("bgra", 4);
1450     g_free (temp);
1451   } else if (strcmp (want, "rgb10a2_le") == 0) {
1452     gchar *temp = want;
1453     orig_want = want = g_strndup ("rgba", 4);
1454     g_free (temp);
1455   }
1456
1457   /* pad want with 'a's */
1458   if ((len = strlen (want)) < 4) {
1459     gchar *new_want = g_strndup (want, 4);
1460     while (len < 4) {
1461       new_want[len] = 'a';
1462       len++;
1463     }
1464     g_free (want);
1465     orig_want = want = new_want;
1466   }
1467
1468   /* pad expect with 'a's */
1469   if ((len = strlen (expect)) < 4) {
1470     gchar *new_expect = g_strndup (expect, 4);
1471     while (len < 4) {
1472       new_expect[len] = 'a';
1473       len++;
1474     }
1475     g_free (expect);
1476     expect = new_expect;
1477   }
1478
1479   /* build the swizzle format */
1480   while (want && want[0] != '\0') {
1481     gchar *val;
1482     gint idx;
1483     gchar needle = want[0];
1484
1485     if (needle == 'x')
1486       needle = 'a';
1487
1488     if (!(val = strchr (expect, needle))
1489         && needle == 'a' && !(val = strchr (expect, 'x')))
1490       goto out;
1491
1492     idx = (gint) (val - expect);
1493
1494     ret = g_string_append_c (ret, _index_to_shader_swizzle (idx));
1495     want = &want[1];
1496   }
1497
1498   discard_output = FALSE;
1499 out:
1500   g_free (orig_want);
1501   g_free (expect);
1502
1503   return g_string_free (ret, discard_output);
1504 }
1505
1506 static guint
1507 _get_n_textures (GstVideoFormat v_format)
1508 {
1509   switch (v_format) {
1510     case GST_VIDEO_FORMAT_RGBA:
1511     case GST_VIDEO_FORMAT_RGBx:
1512     case GST_VIDEO_FORMAT_ARGB:
1513     case GST_VIDEO_FORMAT_xRGB:
1514     case GST_VIDEO_FORMAT_BGRA:
1515     case GST_VIDEO_FORMAT_BGRx:
1516     case GST_VIDEO_FORMAT_ABGR:
1517     case GST_VIDEO_FORMAT_xBGR:
1518     case GST_VIDEO_FORMAT_RGB:
1519     case GST_VIDEO_FORMAT_BGR:
1520     case GST_VIDEO_FORMAT_AYUV:
1521     case GST_VIDEO_FORMAT_VUYA:
1522     case GST_VIDEO_FORMAT_GRAY8:
1523     case GST_VIDEO_FORMAT_GRAY16_LE:
1524     case GST_VIDEO_FORMAT_GRAY16_BE:
1525     case GST_VIDEO_FORMAT_YUY2:
1526     case GST_VIDEO_FORMAT_UYVY:
1527     case GST_VIDEO_FORMAT_RGB16:
1528     case GST_VIDEO_FORMAT_BGR16:
1529     case GST_VIDEO_FORMAT_ARGB64:
1530     case GST_VIDEO_FORMAT_BGR10A2_LE:
1531     case GST_VIDEO_FORMAT_RGB10A2_LE:
1532     case GST_VIDEO_FORMAT_Y410:
1533       return 1;
1534     case GST_VIDEO_FORMAT_NV12:
1535     case GST_VIDEO_FORMAT_NV21:
1536     case GST_VIDEO_FORMAT_NV16:
1537     case GST_VIDEO_FORMAT_NV61:
1538     case GST_VIDEO_FORMAT_P010_10LE:
1539     case GST_VIDEO_FORMAT_P010_10BE:
1540     case GST_VIDEO_FORMAT_P016_LE:
1541     case GST_VIDEO_FORMAT_P016_BE:
1542       return 2;
1543     case GST_VIDEO_FORMAT_I420:
1544     case GST_VIDEO_FORMAT_Y444:
1545     case GST_VIDEO_FORMAT_Y42B:
1546     case GST_VIDEO_FORMAT_Y41B:
1547     case GST_VIDEO_FORMAT_YV12:
1548       return 3;
1549     default:
1550       g_assert_not_reached ();
1551       return 0;
1552   }
1553 }
1554
1555 static void
1556 _RGB_to_RGB (GstGLColorConvert * convert)
1557 {
1558   struct ConvertInfo *info = &convert->priv->convert_info;
1559   GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info);
1560   const gchar *in_format_str = gst_video_format_to_string (in_format);
1561   GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info);
1562   const gchar *out_format_str = gst_video_format_to_string (out_format);
1563   gchar *pixel_order = _RGB_pixel_order (in_format_str, out_format_str);
1564   gchar *alpha = NULL;
1565
1566   if (_is_RGBx (in_format)) {
1567     int i;
1568     char input_alpha_channel = 'a';
1569     for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
1570       if (in_format_str[i] == 'X' || in_format_str[i] == 'x') {
1571         input_alpha_channel = _index_to_shader_swizzle (i);
1572         break;
1573       }
1574     }
1575     alpha = g_strdup_printf ("t.%c = 1.0;", input_alpha_channel);
1576   }
1577   info->templ = &templ_REORDER;
1578   info->frag_body = g_strdup_printf (templ_REORDER_BODY, alpha ? alpha : "",
1579       pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1580   info->shader_tex_names[0] = "tex";
1581
1582   g_free (alpha);
1583   g_free (pixel_order);
1584 }
1585
1586 static void
1587 _YUV_to_RGB (GstGLColorConvert * convert)
1588 {
1589   struct ConvertInfo *info = &convert->priv->convert_info;
1590   GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info);
1591   const gchar *out_format_str = gst_video_format_to_string (out_format);
1592   gchar *pixel_order = _RGB_pixel_order ("rgba", out_format_str);
1593   gboolean apple_ycbcr = gst_gl_context_check_feature (convert->context,
1594       "GL_APPLE_ycbcr_422");
1595   gboolean in_tex_rectangular = FALSE;
1596
1597 #if GST_GL_HAVE_OPENGL
1598   GstMemory *memory = gst_buffer_peek_memory (convert->inbuf, 0);
1599   if (gst_is_gl_memory (memory) && (USING_OPENGL (convert->context)
1600           || USING_OPENGL3 (convert->context))) {
1601     in_tex_rectangular =
1602         convert->priv->from_texture_target == GST_GL_TEXTURE_TARGET_RECTANGLE;
1603   }
1604 #endif
1605
1606   info->out_n_textures = 1;
1607
1608   if (in_tex_rectangular && apple_ycbcr
1609       && gst_buffer_n_memory (convert->inbuf) == 1) {
1610     /* FIXME: We should probably also check if tex_target actually is using
1611      * the Apple YCbCr422 extension. It could also be a normal UYVY texture
1612      * with RB or Lum/Alpha
1613      */
1614     /* The mangling will change this to the correct texture2DRect, sampler2DRect
1615      * for us */
1616     info->templ = &templ_REORDER;
1617     info->frag_body =
1618         g_strdup_printf (templ_REORDER_BODY, "", pixel_order[0], pixel_order[1],
1619         pixel_order[2], pixel_order[3]);
1620     info->shader_tex_names[0] = "tex";
1621   } else {
1622     switch (GST_VIDEO_INFO_FORMAT (&convert->in_info)) {
1623       case GST_VIDEO_FORMAT_AYUV:
1624         info->templ = &templ_AYUV_to_RGB;
1625         info->frag_body = g_strdup_printf (templ_AYUV_to_RGB_BODY, "yzw", 'x',
1626             pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1627         info->shader_tex_names[0] = "tex";
1628         break;
1629       case GST_VIDEO_FORMAT_VUYA:
1630         info->templ = &templ_AYUV_to_RGB;
1631         info->frag_body = g_strdup_printf (templ_AYUV_to_RGB_BODY, "zyx", 'w',
1632             pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1633         info->shader_tex_names[0] = "tex";
1634         break;
1635       case GST_VIDEO_FORMAT_Y410:
1636         info->templ = &templ_AYUV_to_RGB;
1637         info->frag_body = g_strdup_printf (templ_AYUV_to_RGB_BODY, "yxz", 'w',
1638             pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1639         info->shader_tex_names[0] = "tex";
1640         break;
1641       case GST_VIDEO_FORMAT_I420:
1642       case GST_VIDEO_FORMAT_Y444:
1643       case GST_VIDEO_FORMAT_Y42B:
1644       case GST_VIDEO_FORMAT_Y41B:
1645         info->templ = &templ_PLANAR_YUV_to_RGB;
1646         info->frag_body =
1647             g_strdup_printf (templ_PLANAR_YUV_to_RGB_BODY, pixel_order[0],
1648             pixel_order[1], pixel_order[2], pixel_order[3]);
1649         info->shader_tex_names[0] = "Ytex";
1650         info->shader_tex_names[1] = "Utex";
1651         info->shader_tex_names[2] = "Vtex";
1652         break;
1653       case GST_VIDEO_FORMAT_YV12:
1654         info->templ = &templ_PLANAR_YUV_to_RGB;
1655         info->frag_body =
1656             g_strdup_printf (templ_PLANAR_YUV_to_RGB_BODY, pixel_order[0],
1657             pixel_order[1], pixel_order[2], pixel_order[3]);
1658         info->shader_tex_names[0] = "Ytex";
1659         info->shader_tex_names[1] = "Vtex";
1660         info->shader_tex_names[2] = "Utex";
1661         break;
1662       case GST_VIDEO_FORMAT_YUY2:
1663       {
1664         char uv_val = convert->priv->in_tex_formats[0] == GST_GL_RG ? 'g' : 'a';
1665         info->templ = &templ_YUY2_UYVY_to_RGB;
1666         info->frag_body =
1667             g_strdup_printf (templ_YUY2_UYVY_to_RGB_BODY, 'r', uv_val, uv_val,
1668             'g', 'a', pixel_order[0], pixel_order[1], pixel_order[2],
1669             pixel_order[3]);
1670         info->shader_tex_names[0] = "Ytex";
1671         break;
1672       }
1673       case GST_VIDEO_FORMAT_UYVY:
1674       {
1675         char y_val = convert->priv->in_tex_formats[0] == GST_GL_RG ? 'g' : 'a';
1676         info->templ = &templ_YUY2_UYVY_to_RGB;
1677         info->frag_body =
1678             g_strdup_printf (templ_YUY2_UYVY_to_RGB_BODY, y_val, 'g', 'g', 'r',
1679             'b', pixel_order[0], pixel_order[1], pixel_order[2],
1680             pixel_order[3]);
1681         info->shader_tex_names[0] = "Ytex";
1682         break;
1683       }
1684       case GST_VIDEO_FORMAT_NV12:
1685       case GST_VIDEO_FORMAT_NV16:
1686       {
1687         char val2 = convert->priv->in_tex_formats[1] == GST_GL_RG ? 'g' : 'a';
1688         info->templ = &templ_SEMI_PLANAR_to_RGB;
1689         info->frag_body =
1690             g_strdup_printf (templ_SEMI_PLANAR_to_RGB_BODY, 'r', val2,
1691             pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1692         info->shader_tex_names[0] = "Ytex";
1693         info->shader_tex_names[1] = "UVtex";
1694         break;
1695       }
1696       case GST_VIDEO_FORMAT_NV21:
1697       case GST_VIDEO_FORMAT_NV61:
1698       {
1699         char val2 = convert->priv->in_tex_formats[1] == GST_GL_RG ? 'g' : 'a';
1700         info->templ = &templ_SEMI_PLANAR_to_RGB;
1701         info->frag_body =
1702             g_strdup_printf (templ_SEMI_PLANAR_to_RGB_BODY, val2, 'r',
1703             pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1704         info->shader_tex_names[0] = "Ytex";
1705         info->shader_tex_names[1] = "UVtex";
1706         break;
1707       }
1708       case GST_VIDEO_FORMAT_P010_10LE:
1709       case GST_VIDEO_FORMAT_P010_10BE:
1710       case GST_VIDEO_FORMAT_P016_LE:
1711       case GST_VIDEO_FORMAT_P016_BE:
1712       {
1713         info->templ = &templ_SEMI_PLANAR_to_RGB;
1714         info->frag_body =
1715             g_strdup_printf (templ_SEMI_PLANAR_to_RGB_BODY, 'r', 'g',
1716             pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1717         info->shader_tex_names[0] = "Ytex";
1718         info->shader_tex_names[1] = "UVtex";
1719         break;
1720       }
1721       default:
1722         break;
1723     }
1724   }
1725
1726   if (gst_video_colorimetry_matches (&convert->in_info.colorimetry,
1727           GST_VIDEO_COLORIMETRY_BT709)) {
1728     info->cms_offset = (gfloat *) from_yuv_bt709_offset;
1729     info->cms_coeff1 = (gfloat *) from_yuv_bt709_rcoeff;
1730     info->cms_coeff2 = (gfloat *) from_yuv_bt709_gcoeff;
1731     info->cms_coeff3 = (gfloat *) from_yuv_bt709_bcoeff;
1732   } else {
1733     /* defaults/bt601 */
1734     info->cms_offset = (gfloat *) from_yuv_bt601_offset;
1735     info->cms_coeff1 = (gfloat *) from_yuv_bt601_rcoeff;
1736     info->cms_coeff2 = (gfloat *) from_yuv_bt601_gcoeff;
1737     info->cms_coeff3 = (gfloat *) from_yuv_bt601_bcoeff;
1738   }
1739
1740   g_free (pixel_order);
1741 }
1742
1743 static void
1744 _RGB_to_YUV (GstGLColorConvert * convert)
1745 {
1746   struct ConvertInfo *info = &convert->priv->convert_info;
1747   GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info);
1748   const gchar *in_format_str = gst_video_format_to_string (in_format);
1749   GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info);
1750   gchar *pixel_order = _RGB_pixel_order (in_format_str, "rgba");
1751   const gchar *alpha;
1752
1753   info->frag_prog = NULL;
1754
1755   info->shader_tex_names[0] = "tex";
1756
1757   switch (out_format) {
1758     case GST_VIDEO_FORMAT_AYUV:
1759       alpha = _is_RGBx (in_format) ? "1.0" : "texel.a";
1760       info->templ = &templ_RGB_to_AYUV;
1761       info->frag_body = g_strdup_printf (templ_RGB_to_AYUV_BODY, pixel_order[0],
1762           pixel_order[1], pixel_order[2], pixel_order[3], "yzw", 'x', alpha);
1763       info->out_n_textures = 1;
1764       break;
1765     case GST_VIDEO_FORMAT_VUYA:
1766       alpha = _is_RGBx (in_format) ? "1.0" : "texel.a";
1767       info->templ = &templ_RGB_to_AYUV;
1768       info->frag_body = g_strdup_printf (templ_RGB_to_AYUV_BODY, pixel_order[0],
1769           pixel_order[1], pixel_order[2], pixel_order[3], "zyx", 'w', alpha);
1770       info->out_n_textures = 1;
1771       break;
1772     case GST_VIDEO_FORMAT_Y410:
1773       alpha = _is_RGBx (in_format) ? "1.0" : "texel.a";
1774       info->templ = &templ_RGB_to_AYUV;
1775       info->frag_body = g_strdup_printf (templ_RGB_to_AYUV_BODY, pixel_order[0],
1776           pixel_order[1], pixel_order[2], pixel_order[3], "yxz", 'w', alpha);
1777       info->out_n_textures = 1;
1778       break;
1779     case GST_VIDEO_FORMAT_I420:
1780     case GST_VIDEO_FORMAT_YV12:
1781     case GST_VIDEO_FORMAT_Y444:
1782     case GST_VIDEO_FORMAT_Y42B:
1783     case GST_VIDEO_FORMAT_Y41B:
1784       info->templ = &templ_RGB_to_PLANAR_YUV;
1785       info->frag_body = g_strdup_printf (templ_RGB_to_PLANAR_YUV_BODY,
1786           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1787           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1788       info->out_n_textures = 3;
1789       if (out_format == GST_VIDEO_FORMAT_Y444) {
1790         info->chroma_sampling[0] = info->chroma_sampling[1] = 1.0f;
1791       } else if (out_format == GST_VIDEO_FORMAT_Y42B) {
1792         info->chroma_sampling[0] = 2.0f;
1793         info->chroma_sampling[1] = 1.0f;
1794       } else if (out_format == GST_VIDEO_FORMAT_Y41B) {
1795         info->chroma_sampling[0] = 4.0f;
1796         info->chroma_sampling[1] = 1.0f;
1797       } else {
1798         info->chroma_sampling[0] = info->chroma_sampling[1] = 2.0f;
1799       }
1800       break;
1801     case GST_VIDEO_FORMAT_YUY2:
1802       info->templ = &templ_RGB_to_YUY2_UYVY;
1803       info->frag_body = g_strdup_printf (templ_RGB_to_YUY2_UYVY_BODY,
1804           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1805           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1806           'x', 'y', 'x', 'z');
1807       info->out_n_textures = 1;
1808       break;
1809     case GST_VIDEO_FORMAT_UYVY:
1810       info->templ = &templ_RGB_to_YUY2_UYVY,
1811           info->frag_body = g_strdup_printf (templ_RGB_to_YUY2_UYVY_BODY,
1812           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1813           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1814           'y', 'x', 'z', 'x');
1815       info->out_n_textures = 1;
1816       break;
1817     case GST_VIDEO_FORMAT_NV12:
1818     case GST_VIDEO_FORMAT_NV16:
1819       info->templ = &templ_RGB_to_SEMI_PLANAR_YUV,
1820           info->frag_body = g_strdup_printf (templ_RGB_to_SEMI_PLANAR_YUV_BODY,
1821           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1822           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1823           'y', 'z');
1824       info->out_n_textures = 2;
1825       if (out_format == GST_VIDEO_FORMAT_NV16) {
1826         info->chroma_sampling[0] = 2.0f;
1827         info->chroma_sampling[1] = 1.0f;
1828       } else {
1829         info->chroma_sampling[0] = info->chroma_sampling[1] = 2.0f;
1830       }
1831       break;
1832     case GST_VIDEO_FORMAT_NV21:
1833     case GST_VIDEO_FORMAT_NV61:
1834       info->templ = &templ_RGB_to_SEMI_PLANAR_YUV,
1835           info->frag_body = g_strdup_printf (templ_RGB_to_SEMI_PLANAR_YUV_BODY,
1836           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1837           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3],
1838           'z', 'y');
1839       info->out_n_textures = 2;
1840       if (out_format == GST_VIDEO_FORMAT_NV61) {
1841         info->chroma_sampling[0] = 2.0f;
1842         info->chroma_sampling[1] = 1.0f;
1843       } else {
1844         info->chroma_sampling[0] = info->chroma_sampling[1] = 2.0f;
1845       }
1846       break;
1847     default:
1848       break;
1849   }
1850
1851   if (gst_video_colorimetry_matches (&convert->in_info.colorimetry,
1852           GST_VIDEO_COLORIMETRY_BT709)) {
1853     info->cms_offset = (gfloat *) from_rgb_bt709_offset;
1854     info->cms_coeff1 = (gfloat *) from_rgb_bt709_ycoeff;
1855     info->cms_coeff2 = (gfloat *) from_rgb_bt709_ucoeff;
1856     info->cms_coeff3 = (gfloat *) from_rgb_bt709_vcoeff;
1857   } else {
1858     /* defaults/bt601 */
1859     info->cms_offset = (gfloat *) from_rgb_bt601_offset;
1860     info->cms_coeff1 = (gfloat *) from_rgb_bt601_ycoeff;
1861     info->cms_coeff2 = (gfloat *) from_rgb_bt601_ucoeff;
1862     info->cms_coeff3 = (gfloat *) from_rgb_bt601_vcoeff;
1863   }
1864
1865   g_free (pixel_order);
1866 }
1867
1868 static void
1869 _RGB_to_GRAY (GstGLColorConvert * convert)
1870 {
1871   struct ConvertInfo *info = &convert->priv->convert_info;
1872   GstVideoFormat in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info);
1873   const gchar *in_format_str = gst_video_format_to_string (in_format);
1874   gchar *pixel_order = _RGB_pixel_order (in_format_str, "rgba");
1875   gchar *alpha = NULL;
1876
1877   info->out_n_textures = 1;
1878   info->shader_tex_names[0] = "tex";
1879
1880   if (_is_RGBx (in_format))
1881     alpha = g_strdup_printf ("t.%c = 1.0;", pixel_order[3]);
1882
1883   switch (GST_VIDEO_INFO_FORMAT (&convert->out_info)) {
1884     case GST_VIDEO_FORMAT_GRAY8:
1885       info->templ = &templ_REORDER;
1886       info->frag_body = g_strdup_printf (templ_REORDER_BODY, alpha ? alpha : "",
1887           pixel_order[0], pixel_order[0], pixel_order[0], pixel_order[3]);
1888       break;
1889     default:
1890       break;
1891   }
1892
1893   g_free (alpha);
1894   g_free (pixel_order);
1895 }
1896
1897 static void
1898 _GRAY_to_RGB (GstGLColorConvert * convert)
1899 {
1900   struct ConvertInfo *info = &convert->priv->convert_info;
1901   GstVideoFormat out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info);
1902   const gchar *out_format_str = gst_video_format_to_string (out_format);
1903   gchar *pixel_order = _RGB_pixel_order ("rgba", out_format_str);
1904
1905   info->shader_tex_names[0] = "tex";
1906
1907   switch (GST_VIDEO_INFO_FORMAT (&convert->in_info)) {
1908     case GST_VIDEO_FORMAT_GRAY8:
1909       info->templ = &templ_REORDER;
1910       info->frag_body = g_strdup_printf (templ_REORDER_BODY, "", pixel_order[0],
1911           pixel_order[0], pixel_order[0], pixel_order[3]);
1912       break;
1913     case GST_VIDEO_FORMAT_GRAY16_LE:
1914     {
1915       char val2 = convert->priv->in_tex_formats[0] == GST_GL_RG ? 'g' : 'a';
1916       info->templ = &templ_COMPOSE;
1917       info->frag_body = g_strdup_printf (templ_COMPOSE_BODY, val2, 'r',
1918           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1919       break;
1920     }
1921     case GST_VIDEO_FORMAT_GRAY16_BE:
1922     {
1923       char val2 = convert->priv->in_tex_formats[0] == GST_GL_RG ? 'g' : 'a';
1924       info->templ = &templ_COMPOSE;
1925       info->frag_body = g_strdup_printf (templ_COMPOSE_BODY, 'r', val2,
1926           pixel_order[0], pixel_order[1], pixel_order[2], pixel_order[3]);
1927       break;
1928     }
1929     default:
1930       break;
1931   }
1932
1933   g_free (pixel_order);
1934 }
1935
1936 static void
1937 _bind_buffer (GstGLColorConvert * convert)
1938 {
1939   const GstGLFuncs *gl = convert->context->gl_vtable;
1940
1941   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, convert->priv->vbo_indices);
1942   gl->BindBuffer (GL_ARRAY_BUFFER, convert->priv->vertex_buffer);
1943
1944   /* Load the vertex position */
1945   gl->VertexAttribPointer (convert->priv->attr_position, 3, GL_FLOAT, GL_FALSE,
1946       5 * sizeof (GLfloat), (void *) 0);
1947
1948   /* Load the texture coordinate */
1949   gl->VertexAttribPointer (convert->priv->attr_texture, 2, GL_FLOAT, GL_FALSE,
1950       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
1951
1952   gl->EnableVertexAttribArray (convert->priv->attr_position);
1953   gl->EnableVertexAttribArray (convert->priv->attr_texture);
1954 }
1955
1956 static void
1957 _unbind_buffer (GstGLColorConvert * convert)
1958 {
1959   const GstGLFuncs *gl = convert->context->gl_vtable;
1960
1961   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
1962   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1963
1964   gl->DisableVertexAttribArray (convert->priv->attr_position);
1965   gl->DisableVertexAttribArray (convert->priv->attr_texture);
1966 }
1967
1968 static GstGLShader *
1969 _create_shader (GstGLColorConvert * convert)
1970 {
1971   struct ConvertInfo *info = &convert->priv->convert_info;
1972   GString *str = g_string_new (NULL);
1973   GstGLShader *ret = NULL;
1974   GstGLSLStage *stage;
1975   GstGLSLVersion version;
1976   GstGLSLProfile profile;
1977   gchar *version_str, *tmp, *tmp1;
1978   const gchar *strings[3];
1979   GError *error = NULL;
1980   int i;
1981
1982   ret = gst_gl_shader_new (convert->context);
1983
1984   tmp =
1985       _gst_glsl_mangle_shader (text_vertex_shader, GL_VERTEX_SHADER,
1986       info->templ->target, convert->priv->from_texture_target, convert->context,
1987       &version, &profile);
1988
1989   tmp1 = gst_glsl_version_profile_to_string (version, profile);
1990   version_str = g_strdup_printf ("#version %s\n", tmp1);
1991   g_free (tmp1);
1992
1993   strings[0] = version_str;
1994   strings[1] = tmp;
1995   if (!(stage = gst_glsl_stage_new_with_strings (convert->context,
1996               GL_VERTEX_SHADER, version, profile, 2, strings))) {
1997     GST_ERROR_OBJECT (convert, "Failed to create vertex stage");
1998     g_free (version_str);
1999     g_free (tmp);
2000     gst_object_unref (ret);
2001     return NULL;
2002   }
2003   g_free (tmp);
2004
2005   if (!gst_gl_shader_compile_attach_stage (ret, stage, &error)) {
2006     GST_ERROR_OBJECT (convert, "Failed to compile vertex shader %s",
2007         error->message);
2008     g_clear_error (&error);
2009     g_free (version_str);
2010     gst_object_unref (stage);
2011     gst_object_unref (ret);
2012     return NULL;
2013   }
2014
2015   if (info->templ->extensions)
2016     g_string_append (str, info->templ->extensions);
2017
2018   if (convert->priv->from_texture_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES
2019       && info->templ->target != GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
2020     g_string_append (str, glsl_OES_extension_string);
2021
2022   g_string_append (str,
2023       gst_gl_shader_string_get_highest_precision (convert->context, version,
2024           profile));
2025
2026   if (info->templ->uniforms)
2027     g_string_append (str, info->templ->uniforms);
2028
2029   g_string_append_c (str, '\n');
2030
2031   /* GL 3.3+ and GL ES 3.x */
2032   if ((profile == GST_GLSL_PROFILE_CORE && version >= GST_GLSL_VERSION_330)
2033       || (profile == GST_GLSL_PROFILE_ES && version >= GST_GLSL_VERSION_300)) {
2034     if (info->out_n_textures > 1) {
2035       gint i;
2036
2037       for (i = 0; i < info->out_n_textures; i++) {
2038         g_string_append_printf (str,
2039             "layout(location = %d) out vec4 fragColor_%d;\n", i, i);
2040       }
2041     } else {
2042       g_string_append (str, "layout (location = 0) out vec4 fragColor;\n");
2043     }
2044   } else if (profile == GST_GLSL_PROFILE_CORE
2045       && version >= GST_GLSL_VERSION_150) {
2046     /* no layout specifiers, use glBindFragDataLocation instead */
2047     if (info->out_n_textures > 1) {
2048       gint i;
2049
2050       for (i = 0; i < info->out_n_textures; i++) {
2051         gchar *var_name = g_strdup_printf ("fragColor_%d", i);
2052         g_string_append_printf (str, "out vec4 %s;\n", var_name);
2053         gst_gl_shader_bind_frag_data_location (ret, i, var_name);
2054         g_free (var_name);
2055       }
2056     } else {
2057       g_string_append (str, "out vec4 fragColor;\n");
2058       gst_gl_shader_bind_frag_data_location (ret, 0, "fragColor");
2059     }
2060   }
2061
2062   for (i = 0; i < MAX_FUNCTIONS; i++) {
2063     if (info->templ->functions[i] == NULL)
2064       break;
2065
2066     g_string_append_c (str, '\n');
2067     g_string_append (str, info->templ->functions[i]);
2068     g_string_append_c (str, '\n');
2069   }
2070
2071   {
2072     const gchar *varying = NULL;
2073
2074     if ((profile == GST_GLSL_PROFILE_ES && version >= GST_GLSL_VERSION_300)
2075         || (profile == GST_GLSL_PROFILE_CORE
2076             && version >= GST_GLSL_VERSION_150)) {
2077       varying = "in";
2078     } else {
2079       varying = "varying";
2080     }
2081     g_string_append_printf (str, "\n%s vec2 v_texcoord;\nvoid main (void) {\n",
2082         varying);
2083   }
2084   if (info->frag_body) {
2085     g_string_append (str, "vec2 texcoord;\n");
2086     if (convert->priv->from_texture_target == GST_GL_TEXTURE_TARGET_RECTANGLE
2087         && info->templ->target != GST_GL_TEXTURE_TARGET_RECTANGLE) {
2088       g_string_append (str, "texcoord = v_texcoord * vec2 (width, height);\n");
2089     } else {
2090       g_string_append (str, "texcoord = v_texcoord;\n");
2091     }
2092
2093     g_string_append (str, info->frag_body);
2094   }
2095   g_string_append (str, "\n}");
2096   tmp = g_string_free (str, FALSE);
2097   info->frag_prog = _gst_glsl_mangle_shader (tmp, GL_FRAGMENT_SHADER,
2098       info->templ->target, convert->priv->from_texture_target, convert->context,
2099       &version, &profile);
2100   g_free (tmp);
2101
2102   strings[1] = info->frag_prog;
2103   if (!(stage = gst_glsl_stage_new_with_strings (convert->context,
2104               GL_FRAGMENT_SHADER, version, profile, 2, strings))) {
2105     GST_ERROR_OBJECT (convert, "Failed to create fragment stage");
2106     g_free (info->frag_prog);
2107     info->frag_prog = NULL;
2108     g_free (version_str);
2109     gst_object_unref (ret);
2110     return NULL;
2111   }
2112   g_free (version_str);
2113   if (!gst_gl_shader_compile_attach_stage (ret, stage, &error)) {
2114     GST_ERROR_OBJECT (convert, "Failed to compile fragment shader %s",
2115         error->message);
2116     g_clear_error (&error);
2117     g_free (info->frag_prog);
2118     info->frag_prog = NULL;
2119     gst_object_unref (stage);
2120     gst_object_unref (ret);
2121     return NULL;
2122   }
2123
2124   if (!gst_gl_shader_link (ret, &error)) {
2125     GST_ERROR_OBJECT (convert, "Failed to link shader %s", error->message);
2126     g_clear_error (&error);
2127     g_free (info->frag_prog);
2128     info->frag_prog = NULL;
2129     gst_object_unref (ret);
2130     return NULL;
2131   }
2132
2133   return ret;
2134 }
2135
2136 /* Called in the gl thread */
2137 static gboolean
2138 _init_convert (GstGLColorConvert * convert)
2139 {
2140   GstGLFuncs *gl;
2141   struct ConvertInfo *info = &convert->priv->convert_info;
2142   gint i;
2143
2144   gl = convert->context->gl_vtable;
2145
2146   if (convert->initted)
2147     return TRUE;
2148
2149   GST_INFO ("Initializing color conversion from %s to %s",
2150       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)),
2151       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->out_info)));
2152
2153   if (!gl->CreateProgramObject && !gl->CreateProgram) {
2154     GST_ERROR_OBJECT (convert, "Cannot perform color conversion without "
2155         "OpenGL shaders");
2156     goto error;
2157   }
2158
2159   if (GST_VIDEO_INFO_IS_RGB (&convert->in_info)) {
2160     if (GST_VIDEO_INFO_IS_RGB (&convert->out_info)) {
2161       _RGB_to_RGB (convert);
2162     }
2163   }
2164
2165   if (GST_VIDEO_INFO_IS_YUV (&convert->in_info)) {
2166     if (GST_VIDEO_INFO_IS_RGB (&convert->out_info)) {
2167       _YUV_to_RGB (convert);
2168     }
2169   }
2170
2171   if (GST_VIDEO_INFO_IS_RGB (&convert->in_info)) {
2172     if (GST_VIDEO_INFO_IS_YUV (&convert->out_info)) {
2173       _RGB_to_YUV (convert);
2174     }
2175   }
2176
2177   if (GST_VIDEO_INFO_IS_RGB (&convert->in_info)) {
2178     if (GST_VIDEO_INFO_IS_GRAY (&convert->out_info)) {
2179       _RGB_to_GRAY (convert);
2180     }
2181   }
2182
2183   if (GST_VIDEO_INFO_IS_GRAY (&convert->in_info)) {
2184     if (GST_VIDEO_INFO_IS_RGB (&convert->out_info)) {
2185       _GRAY_to_RGB (convert);
2186     }
2187   }
2188
2189   if (!info->frag_body || info->in_n_textures == 0 || info->out_n_textures == 0)
2190     goto unhandled_format;
2191
2192   /* multiple draw targets not supported on GLES2... */
2193   if (info->out_n_textures > 1 && !gl->DrawBuffers) {
2194     GST_ERROR ("Conversion requires output to multiple draw buffers");
2195     goto incompatible_api;
2196   }
2197
2198   /* Requires reading from a RG/LA framebuffer... */
2199   if (USING_GLES2 (convert->context) && !USING_GLES3 (convert->context) &&
2200       (GST_VIDEO_INFO_FORMAT (&convert->out_info) == GST_VIDEO_FORMAT_YUY2 ||
2201           GST_VIDEO_INFO_FORMAT (&convert->out_info) ==
2202           GST_VIDEO_FORMAT_UYVY)) {
2203     GST_ERROR ("Conversion requires reading with an unsupported format");
2204     goto incompatible_api;
2205   }
2206
2207   if (!(convert->shader = _create_shader (convert)))
2208     goto error;
2209
2210   convert->priv->attr_position =
2211       gst_gl_shader_get_attribute_location (convert->shader, "a_position");
2212   convert->priv->attr_texture =
2213       gst_gl_shader_get_attribute_location (convert->shader, "a_texcoord");
2214
2215   gst_gl_shader_use (convert->shader);
2216
2217   if (info->cms_offset && info->cms_coeff1
2218       && info->cms_coeff2 && info->cms_coeff3) {
2219     gst_gl_shader_set_uniform_3fv (convert->shader, "offset", 1,
2220         info->cms_offset);
2221     gst_gl_shader_set_uniform_3fv (convert->shader, "coeff1", 1,
2222         info->cms_coeff1);
2223     gst_gl_shader_set_uniform_3fv (convert->shader, "coeff2", 1,
2224         info->cms_coeff2);
2225     gst_gl_shader_set_uniform_3fv (convert->shader, "coeff3", 1,
2226         info->cms_coeff3);
2227   }
2228
2229   for (i = info->in_n_textures; i >= 0; i--) {
2230     if (info->shader_tex_names[i])
2231       gst_gl_shader_set_uniform_1i (convert->shader, info->shader_tex_names[i],
2232           i);
2233   }
2234
2235   gst_gl_shader_set_uniform_1f (convert->shader, "width",
2236       GST_VIDEO_INFO_WIDTH (&convert->in_info));
2237   gst_gl_shader_set_uniform_1f (convert->shader, "height",
2238       GST_VIDEO_INFO_HEIGHT (&convert->in_info));
2239
2240   if (convert->priv->from_texture_target == GST_GL_TEXTURE_TARGET_RECTANGLE) {
2241     gst_gl_shader_set_uniform_1f (convert->shader, "poffset_x", 1.);
2242     gst_gl_shader_set_uniform_1f (convert->shader, "poffset_y", 1.);
2243   } else {
2244     gst_gl_shader_set_uniform_1f (convert->shader, "poffset_x",
2245         1. / (gfloat) GST_VIDEO_INFO_WIDTH (&convert->in_info));
2246     gst_gl_shader_set_uniform_1f (convert->shader, "poffset_y",
2247         1. / (gfloat) GST_VIDEO_INFO_HEIGHT (&convert->in_info));
2248   }
2249
2250   if (info->chroma_sampling[0] > 0.0f && info->chroma_sampling[1] > 0.0f) {
2251     gst_gl_shader_set_uniform_2fv (convert->shader, "chroma_sampling", 1,
2252         info->chroma_sampling);
2253   }
2254
2255   gst_gl_context_clear_shader (convert->context);
2256
2257   if (convert->fbo == NULL && !_init_convert_fbo (convert)) {
2258     goto error;
2259   }
2260
2261   if (!convert->priv->vertex_buffer) {
2262     if (gl->GenVertexArrays) {
2263       gl->GenVertexArrays (1, &convert->priv->vao);
2264       gl->BindVertexArray (convert->priv->vao);
2265     }
2266
2267     gl->GenBuffers (1, &convert->priv->vertex_buffer);
2268     gl->BindBuffer (GL_ARRAY_BUFFER, convert->priv->vertex_buffer);
2269     gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
2270         GL_STATIC_DRAW);
2271
2272     gl->GenBuffers (1, &convert->priv->vbo_indices);
2273     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, convert->priv->vbo_indices);
2274     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
2275         GL_STATIC_DRAW);
2276
2277     if (gl->GenVertexArrays) {
2278       _bind_buffer (convert);
2279       gl->BindVertexArray (0);
2280     }
2281
2282     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
2283     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
2284   }
2285
2286   gl->BindTexture (GL_TEXTURE_2D, 0);
2287
2288   convert->initted = TRUE;
2289
2290   return TRUE;
2291
2292 unhandled_format:
2293   GST_ERROR_OBJECT (convert, "Don't know how to convert from %s to %s",
2294       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)),
2295       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->out_info)));
2296
2297 error:
2298   return FALSE;
2299
2300 incompatible_api:
2301   {
2302     GST_ERROR_OBJECT (convert, "Converting from %s to %s requires "
2303         "functionality that the current OpenGL setup does not support",
2304         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->in_info)),
2305         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT
2306             (&convert->out_info)));
2307     return FALSE;
2308   }
2309 }
2310
2311 /* called by _init_convert (in the gl thread) */
2312 static gboolean
2313 _init_convert_fbo (GstGLColorConvert * convert)
2314 {
2315   guint out_width, out_height;
2316
2317   out_width = GST_VIDEO_INFO_WIDTH (&convert->out_info);
2318   out_height = GST_VIDEO_INFO_HEIGHT (&convert->out_info);
2319
2320   convert->fbo =
2321       gst_gl_framebuffer_new_with_default_depth (convert->context, out_width,
2322       out_height);
2323
2324   return convert->fbo != NULL;
2325 }
2326
2327 static gboolean
2328 _do_convert_one_view (GstGLContext * context, GstGLColorConvert * convert,
2329     guint view_num)
2330 {
2331   guint in_width, in_height, out_width, out_height;
2332   struct ConvertInfo *c_info = &convert->priv->convert_info;
2333   GstMapInfo out_info[GST_VIDEO_MAX_PLANES], in_info[GST_VIDEO_MAX_PLANES];
2334   gboolean res = TRUE;
2335   gint i, j = 0;
2336   const gint in_plane_offset = view_num * c_info->in_n_textures;
2337   const gint out_plane_offset = view_num * c_info->out_n_textures;
2338
2339   out_width = GST_VIDEO_INFO_WIDTH (&convert->out_info);
2340   out_height = GST_VIDEO_INFO_HEIGHT (&convert->out_info);
2341   in_width = GST_VIDEO_INFO_WIDTH (&convert->in_info);
2342   in_height = GST_VIDEO_INFO_HEIGHT (&convert->in_info);
2343
2344   for (i = 0; i < c_info->in_n_textures; i++) {
2345     convert->priv->in_tex[i] =
2346         (GstGLMemory *) gst_buffer_peek_memory (convert->inbuf,
2347         i + in_plane_offset);
2348     if (!gst_is_gl_memory ((GstMemory *) convert->priv->in_tex[i])) {
2349       GST_ERROR_OBJECT (convert, "input must be GstGLMemory");
2350       res = FALSE;
2351       goto out;
2352     }
2353     if (!gst_memory_map ((GstMemory *) convert->priv->in_tex[i], &in_info[i],
2354             GST_MAP_READ | GST_MAP_GL)) {
2355       GST_ERROR_OBJECT (convert, "failed to map input memory %p",
2356           convert->priv->in_tex[i]);
2357       res = FALSE;
2358       goto out;
2359     }
2360   }
2361
2362   for (j = 0; j < c_info->out_n_textures; j++) {
2363     GstGLMemory *out_tex =
2364         (GstGLMemory *) gst_buffer_peek_memory (convert->outbuf,
2365         j + out_plane_offset);
2366     gint mem_width, mem_height;
2367
2368     if (!gst_is_gl_memory ((GstMemory *) out_tex)) {
2369       GST_ERROR_OBJECT (convert, "output must be GstGLMemory");
2370       res = FALSE;
2371       goto out;
2372     }
2373
2374     mem_width = gst_gl_memory_get_texture_width (out_tex);
2375     mem_height = gst_gl_memory_get_texture_height (out_tex);
2376
2377     if (out_tex->tex_format == GST_GL_LUMINANCE
2378         || out_tex->tex_format == GST_GL_LUMINANCE_ALPHA
2379         || out_width != mem_width || out_height != mem_height) {
2380       /* Luminance formats are not color renderable */
2381       /* rendering to a framebuffer only renders the intersection of all
2382        * the attachments i.e. the smallest attachment size */
2383       if (!convert->priv->out_tex[j]) {
2384         GstGLVideoAllocationParams *params;
2385         GstGLBaseMemoryAllocator *base_mem_allocator;
2386         GstAllocator *allocator;
2387         GstVideoInfo temp_info;
2388
2389         gst_video_info_set_format (&temp_info, GST_VIDEO_FORMAT_RGBA, out_width,
2390             out_height);
2391
2392         allocator = gst_allocator_find (GST_GL_MEMORY_ALLOCATOR_NAME);
2393         base_mem_allocator = GST_GL_BASE_MEMORY_ALLOCATOR (allocator);
2394         params = gst_gl_video_allocation_params_new (context, NULL, &temp_info,
2395             0, NULL, convert->priv->to_texture_target, GST_GL_RGBA);
2396
2397         convert->priv->out_tex[j] =
2398             (GstGLMemory *) gst_gl_base_memory_alloc (base_mem_allocator,
2399             (GstGLAllocationParams *) params);
2400
2401         gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
2402         gst_object_unref (allocator);
2403       }
2404     } else {
2405       convert->priv->out_tex[j] = out_tex;
2406     }
2407
2408     if (!gst_memory_map ((GstMemory *) convert->priv->out_tex[j], &out_info[j],
2409             GST_MAP_WRITE | GST_MAP_GL)) {
2410       GST_ERROR_OBJECT (convert, "failed to map output memory %p",
2411           convert->priv->out_tex[i]);
2412       res = FALSE;
2413       goto out;
2414     }
2415   }
2416
2417   GST_LOG_OBJECT (convert, "converting to textures:%p,%p,%p,%p "
2418       "dimensions:%ux%u, from textures:%p,%p,%p,%p dimensions:%ux%u",
2419       convert->priv->out_tex[0], convert->priv->out_tex[1],
2420       convert->priv->out_tex[2], convert->priv->out_tex[3], out_width,
2421       out_height, convert->priv->in_tex[0], convert->priv->in_tex[1],
2422       convert->priv->in_tex[2], convert->priv->in_tex[3], in_width, in_height);
2423
2424   if (!_do_convert_draw (context, convert))
2425     res = FALSE;
2426
2427 out:
2428   for (j--; j >= 0; j--) {
2429     GstGLMemory *out_tex =
2430         (GstGLMemory *) gst_buffer_peek_memory (convert->outbuf,
2431         j + out_plane_offset);
2432     gint mem_width, mem_height;
2433
2434     gst_memory_unmap ((GstMemory *) convert->priv->out_tex[j], &out_info[j]);
2435
2436     mem_width = gst_gl_memory_get_texture_width (out_tex);
2437     mem_height = gst_gl_memory_get_texture_height (out_tex);
2438
2439     if (out_tex->tex_format == GST_GL_LUMINANCE
2440         || out_tex->tex_format == GST_GL_LUMINANCE_ALPHA
2441         || out_width != mem_width || out_height != mem_height) {
2442       GstMapInfo to_info, from_info;
2443
2444       if (!gst_memory_map ((GstMemory *) convert->priv->out_tex[j], &from_info,
2445               GST_MAP_READ | GST_MAP_GL)) {
2446         GST_ERROR_OBJECT (convert, "Failed to map intermediate memory");
2447         res = FALSE;
2448         continue;
2449       }
2450       if (!gst_memory_map ((GstMemory *) out_tex, &to_info,
2451               GST_MAP_WRITE | GST_MAP_GL)) {
2452         GST_ERROR_OBJECT (convert, "Failed to map intermediate memory");
2453         res = FALSE;
2454         continue;
2455       }
2456       gst_gl_memory_copy_into (convert->priv->out_tex[j],
2457           out_tex->tex_id, convert->priv->to_texture_target,
2458           out_tex->tex_format, mem_width, mem_height);
2459       gst_memory_unmap ((GstMemory *) convert->priv->out_tex[j], &from_info);
2460       gst_memory_unmap ((GstMemory *) out_tex, &to_info);
2461     } else {
2462       convert->priv->out_tex[j] = NULL;
2463     }
2464   }
2465
2466   /* YV12 the same as I420 except planes 1+2 swapped */
2467   if (GST_VIDEO_INFO_FORMAT (&convert->out_info) == GST_VIDEO_FORMAT_YV12) {
2468     GstMemory *mem1 =
2469         gst_buffer_get_memory (convert->outbuf, 1 + out_plane_offset);
2470     GstMemory *mem2 =
2471         gst_buffer_get_memory (convert->outbuf, 2 + out_plane_offset);
2472
2473     gst_buffer_replace_memory (convert->outbuf, 1 + out_plane_offset, mem2);
2474     gst_buffer_replace_memory (convert->outbuf, 2 + out_plane_offset, mem1);
2475   }
2476
2477   for (i--; i >= 0; i--) {
2478     gst_memory_unmap ((GstMemory *) convert->priv->in_tex[i], &in_info[i]);
2479   }
2480
2481   return res;
2482 }
2483
2484 /* Called by the idle function in the gl thread */
2485 void
2486 _do_convert (GstGLContext * context, GstGLColorConvert * convert)
2487 {
2488   GstVideoInfo *in_info = &convert->in_info;
2489   struct ConvertInfo *c_info = &convert->priv->convert_info;
2490   gboolean res = TRUE;
2491   gint views, v;
2492   GstGLSyncMeta *sync_meta;
2493   GstFlowReturn ret;
2494
2495   convert->outbuf = NULL;
2496
2497   if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
2498       GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
2499     views = GST_VIDEO_INFO_VIEWS (in_info);
2500   else
2501     views = 1;
2502
2503   c_info->in_n_textures =
2504       _get_n_textures (GST_VIDEO_INFO_FORMAT (&convert->in_info));
2505   c_info->out_n_textures =
2506       _get_n_textures (GST_VIDEO_INFO_FORMAT (&convert->out_info));
2507
2508   {
2509     gboolean tex_format_change = FALSE;
2510     guint i, v;
2511
2512     for (v = 0; v < views; v++) {
2513       for (i = 0; i < c_info->in_n_textures; i++) {
2514         guint j = v * c_info->in_n_textures + i;
2515         GstGLMemory *in_tex =
2516             (GstGLMemory *) gst_buffer_peek_memory (convert->inbuf, j);
2517         if (!gst_is_gl_memory ((GstMemory *) in_tex)) {
2518           GST_ERROR_OBJECT (convert, "input must be GstGLMemory");
2519           convert->priv->result = FALSE;
2520           return;
2521         }
2522
2523         if (j >= GST_VIDEO_MAX_PLANES)
2524           /* our arrays aren't that big */
2525           g_assert_not_reached ();
2526
2527         if (v > 0 && in_tex->tex_format != convert->priv->in_tex_formats[i]) {
2528           GST_ERROR_OBJECT (convert, "Cannot convert textures with "
2529               "different types");
2530           convert->priv->result = FALSE;
2531           return;
2532         }
2533
2534         if (convert->priv->in_tex_formats[j] != in_tex->tex_format)
2535           tex_format_change = TRUE;
2536
2537         convert->priv->in_tex_formats[j] = in_tex->tex_format;
2538       }
2539     }
2540
2541     if (tex_format_change)
2542       gst_gl_color_convert_reset_shader (convert);
2543   }
2544
2545   if (!_init_convert (convert)) {
2546     convert->priv->result = FALSE;
2547     return;
2548   }
2549
2550   sync_meta = gst_buffer_get_gl_sync_meta (convert->inbuf);
2551   if (sync_meta)
2552     gst_gl_sync_meta_wait (sync_meta, convert->context);
2553
2554   if (!convert->priv->pool) {
2555     gboolean ret;
2556     /* No pool! */
2557     GstQuery *query = gst_query_new_allocation (convert->priv->out_caps, TRUE);
2558     ret = gst_gl_color_convert_decide_allocation (convert, query);
2559     gst_query_unref (query);
2560
2561     if (!ret) {
2562       GST_ERROR_OBJECT (convert, "Failed to choose allocation parameters");
2563       convert->priv->result = FALSE;
2564       return;
2565     }
2566
2567     if (!convert->priv->pool) {
2568       GST_ERROR_OBJECT (convert, "Failed to create a buffer pool");
2569       convert->priv->result = FALSE;
2570       return;
2571     }
2572   }
2573
2574   if (!convert->priv->pool_started) {
2575     if (!gst_buffer_pool_set_active (convert->priv->pool, TRUE)) {
2576       GST_ERROR_OBJECT (convert, "Failed to start buffer pool");
2577       convert->priv->result = FALSE;
2578       return;
2579     }
2580     convert->priv->pool_started = TRUE;
2581   }
2582
2583   ret =
2584       gst_buffer_pool_acquire_buffer (convert->priv->pool, &convert->outbuf,
2585       NULL);
2586   if (ret != GST_FLOW_OK) {
2587     GST_ERROR_OBJECT (convert, "Failed to acquire buffer from pool: %s",
2588         gst_flow_get_name (ret));
2589     convert->priv->result = FALSE;
2590     return;
2591   }
2592
2593   gst_gl_insert_debug_marker (context, "%s converting from %s to %s",
2594       GST_OBJECT_NAME (convert),
2595       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)),
2596       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&convert->out_info)));
2597   /* Handle all views on input and output one at a time */
2598   for (v = 0; res && v < views; v++)
2599     res = _do_convert_one_view (context, convert, v);
2600
2601   if (!res) {
2602     gst_buffer_unref (convert->outbuf);
2603     convert->outbuf = NULL;
2604   }
2605
2606   if (convert->outbuf) {
2607     GstVideoOverlayCompositionMeta *composition_meta;
2608     GstGLSyncMeta *sync_meta =
2609         gst_buffer_add_gl_sync_meta (convert->context, convert->outbuf);
2610
2611     if (sync_meta)
2612       gst_gl_sync_meta_set_sync_point (sync_meta, convert->context);
2613
2614     composition_meta =
2615         gst_buffer_get_video_overlay_composition_meta (convert->inbuf);
2616     if (composition_meta) {
2617       GST_DEBUG ("found video overlay composition meta, applying on output.");
2618       gst_buffer_add_video_overlay_composition_meta
2619           (convert->outbuf, composition_meta->overlay);
2620     }
2621   }
2622
2623   convert->priv->result = res;
2624   return;
2625 }
2626
2627 static gboolean
2628 _do_convert_draw (GstGLContext * context, GstGLColorConvert * convert)
2629 {
2630   GstGLFuncs *gl;
2631   struct ConvertInfo *c_info = &convert->priv->convert_info;
2632   guint out_width, out_height;
2633   gint i;
2634   gboolean ret = TRUE;
2635
2636   GLenum multipleRT[] = {
2637     GL_COLOR_ATTACHMENT0,
2638     GL_COLOR_ATTACHMENT1,
2639     GL_COLOR_ATTACHMENT2
2640   };
2641
2642   gl = context->gl_vtable;
2643
2644   gst_gl_framebuffer_bind (convert->fbo);
2645
2646   /* attach the texture to the FBO to renderer to */
2647   for (i = 0; i < c_info->out_n_textures; i++) {
2648     GstGLBaseMemory *tex = (GstGLBaseMemory *) convert->priv->out_tex[i];
2649
2650     gst_gl_framebuffer_attach (convert->fbo, GL_COLOR_ATTACHMENT0 + i, tex);
2651   }
2652
2653   if (gl->DrawBuffers)
2654     gl->DrawBuffers (c_info->out_n_textures, multipleRT);
2655   else if (gl->DrawBuffer)
2656     gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
2657
2658   gst_gl_framebuffer_get_effective_dimensions (convert->fbo, &out_width,
2659       &out_height);
2660   gl->Viewport (0, 0, out_width, out_height);
2661
2662   gst_gl_shader_use (convert->shader);
2663
2664   if (gl->BindVertexArray)
2665     gl->BindVertexArray (convert->priv->vao);
2666   _bind_buffer (convert);
2667
2668   for (i = c_info->in_n_textures - 1; i >= 0; i--) {
2669     gchar *scale_name = g_strdup_printf ("tex_scale%u", i);
2670     guint gl_target =
2671         gst_gl_texture_target_to_gl (convert->priv->from_texture_target);
2672
2673     gl->ActiveTexture (GL_TEXTURE0 + i);
2674     gl->BindTexture (gl_target, convert->priv->in_tex[i]->tex_id);
2675     gl->TexParameteri (gl_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2676     gl->TexParameteri (gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2677     gl->TexParameteri (gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2678     gl->TexParameteri (gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2679
2680     gst_gl_shader_set_uniform_2fv (convert->shader, scale_name, 1,
2681         convert->priv->in_tex[i]->tex_scaling);
2682
2683     g_free (scale_name);
2684   }
2685
2686   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
2687
2688   if (gl->BindVertexArray)
2689     gl->BindVertexArray (0);
2690   else
2691     _unbind_buffer (convert);
2692
2693   if (gl->DrawBuffer)
2694     gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
2695
2696   /* we are done with the shader */
2697   gst_gl_context_clear_shader (context);
2698
2699   if (!gst_gl_context_check_framebuffer_status (context, GL_FRAMEBUFFER))
2700     ret = FALSE;
2701
2702   gst_gl_context_clear_framebuffer (context);
2703
2704   return ret;
2705 }