• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2009 Julien Isorce <julien.isorce@mail.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 /**
22  * SECTION:element-gldeinterlace
23  * @title: gldeinterlace
24  *
25  * Deinterlace video using OpenGL fragment shaders.
26  *
27  * ## Examples
28  * |[
29  * gst-launch-1.0 videotestsrc ! glupload ! gldeinterlace ! glimagesink
30  * ]|
31  * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required.
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include <gst/gl/gstglfuncs.h>
40 
41 #include "gstglelements.h"
42 #include "gstgldeinterlace.h"
43 
44 #define GST_CAT_DEFAULT gst_gl_deinterlace_debug
45 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
46 
47 enum
48 {
49   PROP_0,
50   PROP_METHOD
51 };
52 
53 #define DEBUG_INIT \
54   GST_DEBUG_CATEGORY_INIT (gst_gl_deinterlace_debug, "gldeinterlace", 0, "gldeinterlace element");
55 #define gst_gl_deinterlace_parent_class parent_class
56 G_DEFINE_TYPE_WITH_CODE (GstGLDeinterlace, gst_gl_deinterlace,
57     GST_TYPE_GL_FILTER, DEBUG_INIT);
58 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (gldeinterlace, "gldeinterlace",
59     GST_RANK_NONE, GST_TYPE_GL_DEINTERLACE, gl_element_init (plugin));
60 
61 static void gst_gl_deinterlace_set_property (GObject * object,
62     guint prop_id, const GValue * value, GParamSpec * pspec);
63 static void gst_gl_deinterlace_get_property (GObject * object,
64     guint prop_id, GValue * value, GParamSpec * pspec);
65 
66 static gboolean gst_gl_deinterlace_start (GstBaseTransform * trans);
67 static gboolean gst_gl_deinterlace_reset (GstBaseTransform * trans);
68 static GstCaps *gst_gl_deinterlace_transform_internal_caps (GstGLFilter *
69     filter, GstPadDirection direction, GstCaps * caps, GstCaps * caps_filter);
70 static gboolean gst_gl_deinterlace_init_fbo (GstGLFilter * filter);
71 static gboolean gst_gl_deinterlace_filter (GstGLFilter * filter,
72     GstBuffer * inbuf, GstBuffer * outbuf);
73 static gboolean gst_gl_deinterlace_filter_texture (GstGLFilter * filter,
74     GstGLMemory * in_tex, GstGLMemory * out_tex);
75 static gboolean gst_gl_deinterlace_vfir_callback (GstGLFilter * filter,
76     GstGLMemory * in_tex, gpointer stuff);
77 static gboolean gst_gl_deinterlace_greedyh_callback (GstGLFilter * filter,
78     GstGLMemory * in_tex, gpointer stuff);
79 
80 /* *INDENT-OFF* */
81 static const gchar *greedyh_fragment_source =
82   "uniform sampler2D tex;\n"
83   "uniform sampler2D tex_prev;\n"
84   "uniform float max_comb;\n"
85   "uniform float motion_threshold;\n"
86   "uniform float motion_sense;\n"
87   "uniform float width;\n"
88   "uniform float height;\n"
89   "varying vec2 v_texcoord;\n"
90 
91   "void main () {\n"
92   "  if (int(mod(v_texcoord.y * height, 2.0)) == 0) {\n"
93   "    gl_FragColor = vec4(texture2D(tex_prev, v_texcoord).rgb, 1.0);\n"
94   "  } else {\n"
95   "    vec2 texcoord_L1_a1, texcoord_L3_a1, texcoord_L1, texcoord_L3, texcoord_L1_1, texcoord_L3_1;\n"
96   "    vec3 L1_a1, L3_a1, L1, L3, L1_1, L3_1;\n"
97 
98   "    texcoord_L1 = vec2(v_texcoord.x, v_texcoord.y - 1.0 / height);\n"
99   "    texcoord_L3 = vec2(v_texcoord.x, v_texcoord.y + 1.0 / height);\n"
100   "    L1 = texture2D(tex_prev, texcoord_L1).rgb;\n"
101   "    L3 = texture2D(tex_prev, texcoord_L3).rgb;\n"
102   "    if (v_texcoord.x == 1.0 && v_texcoord.y == 1.0) {\n"
103   "      L1_1 = L1;\n"
104   "      L3_1 = L3;\n"
105   "    } else {\n"
106   "      texcoord_L1_1 = vec2(v_texcoord.x + 1.0 / width, v_texcoord.y - 1.0 / height);\n"
107   "      texcoord_L3_1 = vec2(v_texcoord.x + 1.0 / width, v_texcoord.y + 1.0 / height);\n"
108   "      L1_1 = texture2D(tex_prev, texcoord_L1_1).rgb;\n"
109   "      L3_1 = texture2D(tex_prev, texcoord_L3_1).rgb;\n"
110   "    }\n"
111 
112   "    if (int(ceil(v_texcoord.x + v_texcoord.y)) == 0) {\n"
113   "      L1_a1 = L1;\n"
114   "      L3_a1 = L3;\n"
115   "    } else {\n"
116   "      texcoord_L1_a1 = vec2(v_texcoord.x - 1.0 / width, v_texcoord.y - 1.0 / height);\n"
117   "      texcoord_L3_a1 = vec2(v_texcoord.x - 1.0 / width, v_texcoord.y + 1.0 / height);\n"
118   "      L1_a1 = texture2D(tex_prev, texcoord_L1_a1).rgb;\n"
119   "      L3_a1 = texture2D(tex_prev, texcoord_L3_a1).rgb;\n"
120   "    }\n"
121           //STEP 1
122   "    vec3 avg_a1 = (L1_a1 + L3_a1) / 2.0;\n"
123   "    vec3 avg = (L1 + L3) / 2.0;\n"
124   "    vec3 avg_1 = (L1_1 + L3_1) / 2.0;\n"
125   "    vec3 avg_s = (avg_a1 + avg_1) / 2.0;\n"
126   "    vec3 avg_sc = (avg_s + avg) / 2.0;\n"
127   "    vec3 L2 = texture2D(tex, v_texcoord).rgb;\n"
128   "    vec3 LP2 = texture2D(tex_prev, v_texcoord).rgb;\n"
129   "    vec3 best;\n"
130   "    if (abs(L2.r - avg_sc.r) < abs(LP2.r - avg_sc.r)) {\n"
131   "      best.r = L2.r;\n" "    } else {\n"
132   "      best.r = LP2.r;\n"
133   "    }\n"
134 
135   "    if (abs(L2.g - avg_sc.g) < abs(LP2.g - avg_sc.g)) {\n"
136   "      best.g = L2.g;\n"
137   "    } else {\n"
138   "      best.g = LP2.g;\n"
139   "    }\n"
140 
141   "    if (abs(L2.b - avg_sc.b) < abs(LP2.b - avg_sc.b)) {\n"
142   "      best.b = L2.b;\n"
143   "    } else {\n"
144   "      best.b = LP2.b;\n"
145   "    }\n"
146           //STEP 2
147   "    vec3 last;\n"
148   "    last.r = clamp(best.r, max(min(L1.r, L3.r) - max_comb, 0.0), min(max(L1.r, L3.r) + max_comb, 1.0));\n"
149   "    last.g = clamp(best.g, max(min(L1.g, L3.g) - max_comb, 0.0), min(max(L1.g, L3.g) + max_comb, 1.0));\n"
150   "    last.b = clamp(best.b, max(min(L1.b, L3.b) - max_comb, 0.0), min(max(L1.b, L3.b) + max_comb, 1.0));\n"
151           //STEP 3
152   "    const vec3 luma = vec3 (0.299011, 0.586987, 0.114001);"
153   "    float mov = min(max(abs(dot(L2 - LP2, luma)) - motion_threshold, 0.0) * motion_sense, 1.0);\n"
154   "    last = last * (1.0 - mov) + avg_sc * mov;\n"
155   "    gl_FragColor = vec4(last, 1.0);\n"
156   "  }\n"
157   "}\n";
158 
159 const gchar *vfir_fragment_source =
160   "uniform sampler2D tex;\n"
161   "uniform float width;\n"
162   "uniform float height;\n"
163   "varying vec2 v_texcoord;\n"
164   "void main()\n"
165   "{\n"
166   "  vec2 topcoord, botcoord;\n"
167   "  vec4 cur_color, top_color, bot_color;\n"
168   "  topcoord.x = v_texcoord.x;\n"
169   "  botcoord.x = v_texcoord.x;\n"
170   "  if (v_texcoord.y == 0.0 || v_texcoord.y == 1.0) {\n"
171   "    topcoord.y = v_texcoord.y ;\n"
172   "    botcoord.y = v_texcoord.y ;\n"
173   "  }\n"
174   "  else {\n"
175   "    topcoord.y = v_texcoord.y - 1.0/height;\n"
176   "    botcoord.y = v_texcoord.y + 1.0/height;\n"
177   "  }\n"
178   "  cur_color = texture2D(tex, v_texcoord);\n"
179   "  top_color = texture2D(tex, topcoord);\n"
180   "  bot_color = texture2D(tex, botcoord);\n"
181   "  gl_FragColor = 0.5*cur_color + 0.25*top_color + 0.25*bot_color;\n"
182   "}";
183 /* *INDENT-ON* */
184 
185 /* dont' forget to edit the following when a new method is added */
186 typedef enum
187 {
188   GST_GL_DEINTERLACE_VFIR,
189   GST_GL_DEINTERLACE_GREEDYH
190 } GstGLDeinterlaceMethod;
191 
192 static const GEnumValue *
gst_gl_deinterlace_get_methods(void)193 gst_gl_deinterlace_get_methods (void)
194 {
195   static const GEnumValue method_types[] = {
196     {GST_GL_DEINTERLACE_VFIR, "Blur Vertical", "vfir"},
197     {GST_GL_DEINTERLACE_GREEDYH, "Motion Adaptive: Advanced Detection",
198         "greedyh"},
199     {0, NULL, NULL}
200   };
201   return method_types;
202 }
203 
204 #define GST_TYPE_GL_DEINTERLACE_METHODS (gst_gl_deinterlace_method_get_type ())
205 static GType
gst_gl_deinterlace_method_get_type(void)206 gst_gl_deinterlace_method_get_type (void)
207 {
208   static GType gl_deinterlace_method_type = 0;
209   if (!gl_deinterlace_method_type) {
210     gl_deinterlace_method_type =
211         g_enum_register_static ("GstGLDeinterlaceMethod",
212         gst_gl_deinterlace_get_methods ());
213   }
214   return gl_deinterlace_method_type;
215 }
216 
217 static void
gst_gl_deinterlace_set_method(GstGLDeinterlace * deinterlace,guint method_types)218 gst_gl_deinterlace_set_method (GstGLDeinterlace * deinterlace,
219     guint method_types)
220 {
221   switch (method_types) {
222     case GST_GL_DEINTERLACE_VFIR:
223       deinterlace->deinterlacefunc = gst_gl_deinterlace_vfir_callback;
224       deinterlace->current_method = method_types;
225       break;
226     case GST_GL_DEINTERLACE_GREEDYH:
227       deinterlace->deinterlacefunc = gst_gl_deinterlace_greedyh_callback;
228       deinterlace->current_method = method_types;
229       break;
230     default:
231       g_assert_not_reached ();
232       break;
233   }
234 }
235 
236 static void
gst_gl_deinterlace_class_init(GstGLDeinterlaceClass * klass)237 gst_gl_deinterlace_class_init (GstGLDeinterlaceClass * klass)
238 {
239   GObjectClass *gobject_class;
240   GstElementClass *element_class;
241 
242   gobject_class = (GObjectClass *) klass;
243   element_class = GST_ELEMENT_CLASS (klass);
244 
245   gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
246 
247   gobject_class->set_property = gst_gl_deinterlace_set_property;
248   gobject_class->get_property = gst_gl_deinterlace_get_property;
249 
250   gst_element_class_set_metadata (element_class,
251       "OpenGL deinterlacing filter", "Deinterlace",
252       "Deinterlacing based on fragment shaders",
253       "Julien Isorce <julien.isorce@mail.com>");
254 
255   g_object_class_install_property (gobject_class,
256       PROP_METHOD,
257       g_param_spec_enum ("method",
258           "Deinterlace Method",
259           "Select which deinterlace method apply to GL video texture",
260           GST_TYPE_GL_DEINTERLACE_METHODS,
261           GST_GL_DEINTERLACE_VFIR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
262 
263   GST_BASE_TRANSFORM_CLASS (klass)->start = gst_gl_deinterlace_start;
264   GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_deinterlace_reset;
265 
266   GST_GL_FILTER_CLASS (klass)->transform_internal_caps =
267       gst_gl_deinterlace_transform_internal_caps;
268   GST_GL_FILTER_CLASS (klass)->filter = gst_gl_deinterlace_filter;
269   GST_GL_FILTER_CLASS (klass)->filter_texture =
270       gst_gl_deinterlace_filter_texture;
271   GST_GL_FILTER_CLASS (klass)->init_fbo = gst_gl_deinterlace_init_fbo;
272 
273   GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
274       GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
275 
276   gst_type_mark_as_plugin_api (GST_TYPE_GL_DEINTERLACE_METHODS, 0);
277 }
278 
279 static void
gst_gl_deinterlace_init(GstGLDeinterlace * filter)280 gst_gl_deinterlace_init (GstGLDeinterlace * filter)
281 {
282   filter->shaderstable = NULL;
283   filter->deinterlacefunc = gst_gl_deinterlace_vfir_callback;
284   filter->current_method = GST_GL_DEINTERLACE_VFIR;
285   filter->prev_buffer = NULL;
286   filter->prev_tex = NULL;
287 }
288 
289 static gboolean
gst_gl_deinterlace_start(GstBaseTransform * trans)290 gst_gl_deinterlace_start (GstBaseTransform * trans)
291 {
292   GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (trans);
293 
294   deinterlace_filter->shaderstable = g_hash_table_new (g_str_hash, g_str_equal);
295 
296   return GST_BASE_TRANSFORM_CLASS (parent_class)->start (trans);
297 }
298 
299 static void
gst_gl_deinterlace_ghash_func_clean(gpointer key,gpointer value,gpointer data)300 gst_gl_deinterlace_ghash_func_clean (gpointer key, gpointer value,
301     gpointer data)
302 {
303   GstGLShader *shader = (GstGLShader *) value;
304 
305   gst_object_unref (shader);
306 
307   value = NULL;
308 }
309 
310 static gboolean
gst_gl_deinterlace_reset(GstBaseTransform * trans)311 gst_gl_deinterlace_reset (GstBaseTransform * trans)
312 {
313   GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (trans);
314 
315   gst_buffer_replace (&deinterlace_filter->prev_buffer, NULL);
316 
317   //blocking call, wait the opengl thread has destroyed the shader
318   if (deinterlace_filter->shaderstable) {
319     /* release shaders in the gl thread */
320     g_hash_table_foreach (deinterlace_filter->shaderstable,
321         gst_gl_deinterlace_ghash_func_clean, deinterlace_filter);
322 
323     /* clean the htable without calling values destructors
324      * because shaders have been released in the glthread
325      * through the foreach func */
326     g_hash_table_unref (deinterlace_filter->shaderstable);
327     deinterlace_filter->shaderstable = NULL;
328   }
329 
330   return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (trans);
331 }
332 
333 static GstCaps *
gst_gl_deinterlace_transform_internal_caps(GstGLFilter * filter,GstPadDirection direction,GstCaps * caps,GstCaps * caps_filter)334 gst_gl_deinterlace_transform_internal_caps (GstGLFilter * filter,
335     GstPadDirection direction, GstCaps * caps, GstCaps * caps_filter)
336 {
337   gint len;
338   GstCaps *res;
339   GstStructure *s;
340 
341   res = gst_caps_copy (caps);
342 
343   for (len = gst_caps_get_size (res); len > 0; len--) {
344     s = gst_caps_get_structure (res, len - 1);
345     if (direction == GST_PAD_SINK) {
346       gst_structure_remove_field (s, "interlace-mode");
347     }
348   }
349 
350   return res;
351 }
352 
353 static void
gst_gl_deinterlace_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)354 gst_gl_deinterlace_set_property (GObject * object, guint prop_id,
355     const GValue * value, GParamSpec * pspec)
356 {
357   GstGLDeinterlace *filter = GST_GL_DEINTERLACE (object);
358 
359   switch (prop_id) {
360     case PROP_METHOD:
361       gst_gl_deinterlace_set_method (filter, g_value_get_enum (value));
362       break;
363     default:
364       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365       break;
366   }
367 }
368 
369 static void
gst_gl_deinterlace_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)370 gst_gl_deinterlace_get_property (GObject * object, guint prop_id,
371     GValue * value, GParamSpec * pspec)
372 {
373   GstGLDeinterlace *filter = GST_GL_DEINTERLACE (object);
374 
375   switch (prop_id) {
376     case PROP_METHOD:
377       g_value_set_enum (value, filter->current_method);
378       break;
379     default:
380       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
381       break;
382   }
383 }
384 
385 static gboolean
gst_gl_deinterlace_init_fbo(GstGLFilter * filter)386 gst_gl_deinterlace_init_fbo (GstGLFilter * filter)
387 {
388   return TRUE;
389 }
390 
391 static gboolean
gst_gl_deinterlace_filter_texture(GstGLFilter * filter,GstGLMemory * in_tex,GstGLMemory * out_tex)392 gst_gl_deinterlace_filter_texture (GstGLFilter * filter, GstGLMemory * in_tex,
393     GstGLMemory * out_tex)
394 {
395   GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter);
396 
397   //blocking call, use a FBO
398   gst_gl_filter_render_to_target (filter, in_tex, out_tex,
399       deinterlace_filter->deinterlacefunc, deinterlace_filter);
400 
401   return TRUE;
402 }
403 
404 static gboolean
gst_gl_deinterlace_filter(GstGLFilter * filter,GstBuffer * inbuf,GstBuffer * outbuf)405 gst_gl_deinterlace_filter (GstGLFilter * filter, GstBuffer * inbuf,
406     GstBuffer * outbuf)
407 {
408   GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter);
409 
410   gst_gl_filter_filter_texture (filter, inbuf, outbuf);
411 
412   gst_buffer_replace (&deinterlace_filter->prev_buffer, inbuf);
413 
414   return TRUE;
415 }
416 
417 static GstGLShader *
gst_gl_deinterlace_get_fragment_shader(GstGLFilter * filter,const gchar * shader_name,const gchar * shader_source)418 gst_gl_deinterlace_get_fragment_shader (GstGLFilter * filter,
419     const gchar * shader_name, const gchar * shader_source)
420 {
421   GstGLShader *shader = NULL;
422   GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter);
423   GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
424   const gchar *frags[2];
425 
426   shader = g_hash_table_lookup (deinterlace_filter->shaderstable, shader_name);
427 
428   frags[0] =
429       gst_gl_shader_string_get_highest_precision (context,
430       GST_GLSL_VERSION_NONE,
431       GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
432   frags[1] = shader_source;
433 
434   if (!shader) {
435     GError *error = NULL;
436 
437     if (!(shader = gst_gl_shader_new_link_with_stages (context, &error,
438                 gst_glsl_stage_new_default_vertex (context),
439                 gst_glsl_stage_new_with_strings (context, GL_FRAGMENT_SHADER,
440                     GST_GLSL_VERSION_NONE,
441                     GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, 2,
442                     frags), NULL))) {
443       GST_ELEMENT_ERROR (deinterlace_filter, RESOURCE, NOT_FOUND,
444           ("Failed to initialize %s shader", shader_name), (NULL));
445     }
446 
447     filter->draw_attr_position_loc =
448         gst_gl_shader_get_attribute_location (shader, "a_position");
449     filter->draw_attr_texture_loc =
450         gst_gl_shader_get_attribute_location (shader, "a_texcoord");
451   }
452 
453   g_hash_table_insert (deinterlace_filter->shaderstable, (gchar *) shader_name,
454       shader);
455 
456   return shader;
457 }
458 
459 static gboolean
gst_gl_deinterlace_vfir_callback(GstGLFilter * filter,GstGLMemory * in_tex,gpointer user_data)460 gst_gl_deinterlace_vfir_callback (GstGLFilter * filter, GstGLMemory * in_tex,
461     gpointer user_data)
462 {
463   GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
464   const GstGLFuncs *gl = context->gl_vtable;
465   GstGLShader *shader;
466 
467   shader = gst_gl_deinterlace_get_fragment_shader (filter, "vfir",
468       vfir_fragment_source);
469 
470   if (!shader)
471     return FALSE;
472 
473 #if GST_GL_HAVE_OPENGL
474   if (USING_OPENGL (context)) {
475     gl->MatrixMode (GL_PROJECTION);
476     gl->LoadIdentity ();
477   }
478 #endif
479 
480   gst_gl_shader_use (shader);
481 
482   gl->ActiveTexture (GL_TEXTURE0);
483   gl->BindTexture (GL_TEXTURE_2D, gst_gl_memory_get_texture_id (in_tex));
484 
485   gst_gl_shader_set_uniform_1i (shader, "tex", 0);
486   gst_gl_shader_set_uniform_1f (shader, "width",
487       GST_VIDEO_INFO_WIDTH (&filter->out_info));
488   gst_gl_shader_set_uniform_1f (shader, "height",
489       GST_VIDEO_INFO_HEIGHT (&filter->out_info));
490 
491   gst_gl_filter_draw_fullscreen_quad (filter);
492 
493   return TRUE;
494 }
495 
496 static gboolean
gst_gl_deinterlace_greedyh_callback(GstGLFilter * filter,GstGLMemory * in_tex,gpointer user_data)497 gst_gl_deinterlace_greedyh_callback (GstGLFilter * filter, GstGLMemory * in_tex,
498     gpointer user_data)
499 {
500   GstGLShader *shader;
501   GstGLDeinterlace *deinterlace_filter = GST_GL_DEINTERLACE (filter);
502   GstGLContext *context = GST_GL_BASE_FILTER (filter)->context;
503   GstGLFuncs *gl = context->gl_vtable;
504 
505   shader =
506       gst_gl_deinterlace_get_fragment_shader (filter, "greedhy",
507       greedyh_fragment_source);
508 
509   if (!shader)
510     return FALSE;
511 
512 #if GST_GL_HAVE_OPENGL
513   if (USING_OPENGL (context)) {
514     gl->MatrixMode (GL_PROJECTION);
515     gl->LoadIdentity ();
516   }
517 #endif
518 
519   gst_gl_shader_use (shader);
520 
521   if (G_LIKELY (deinterlace_filter->prev_tex != NULL)) {
522     gl->ActiveTexture (GL_TEXTURE1);
523     gst_gl_shader_set_uniform_1i (shader, "tex_prev", 1);
524     gl->BindTexture (GL_TEXTURE_2D,
525         gst_gl_memory_get_texture_id (deinterlace_filter->prev_tex));
526   }
527 
528   gl->ActiveTexture (GL_TEXTURE0);
529   gl->BindTexture (GL_TEXTURE_2D, gst_gl_memory_get_texture_id (in_tex));
530 
531   gst_gl_shader_set_uniform_1i (shader, "tex", 0);
532   gst_gl_shader_set_uniform_1f (shader, "max_comb", 5.0f / 255.0f);
533   gst_gl_shader_set_uniform_1f (shader, "motion_threshold", 25.0f / 255.0f);
534   gst_gl_shader_set_uniform_1f (shader, "motion_sense", 30.0f / 255.0f);
535 
536   gst_gl_shader_set_uniform_1f (shader, "width",
537       GST_VIDEO_INFO_WIDTH (&filter->out_info));
538   gst_gl_shader_set_uniform_1f (shader, "height",
539       GST_VIDEO_INFO_HEIGHT (&filter->out_info));
540 
541   gst_gl_filter_draw_fullscreen_quad (filter);
542 
543   /* we keep the previous buffer around so this is safe */
544   deinterlace_filter->prev_tex = in_tex;
545 
546   return TRUE;
547 }
548