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