• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@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 /**
22  * SECTION:element-gloverlay
23  * @title: gloverlay
24  *
25  * Overlay GL video texture with a PNG image
26  *
27  * ## Examples
28  * |[
29  * gst-launch-1.0 videotestsrc ! gloverlay location=image.jpg ! glimagesink
30  * ]|
31  * FBO (Frame Buffer Object) is required.
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include <gst/base/gsttypefindhelper.h>
40 #include <gst/gl/gstglconfig.h>
41 
42 #include "gstglelements.h"
43 #include "gstgloverlay.h"
44 #include "effects/gstgleffectssources.h"
45 #include "gstglutils.h"
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #if defined(_MSC_VER) || (defined (__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 6)
50 #define HAVE_BOOLEAN
51 #endif
52 #include <jpeglib.h>
53 #include <png.h>
54 
55 #if PNG_LIBPNG_VER >= 10400
56 #define int_p_NULL         NULL
57 #define png_infopp_NULL    NULL
58 #endif
59 
60 #define GST_CAT_DEFAULT gst_gl_overlay_debug
61 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
62 
63 #define DEBUG_INIT \
64   GST_DEBUG_CATEGORY_INIT (gst_gl_overlay_debug, "gloverlay", 0, "gloverlay element");
65 
66 #define gst_gl_overlay_parent_class parent_class
67 G_DEFINE_TYPE_WITH_CODE (GstGLOverlay, gst_gl_overlay, GST_TYPE_GL_FILTER,
68     DEBUG_INIT);
69 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (gloverlay, "gloverlay",
70     GST_RANK_NONE, GST_TYPE_GL_OVERLAY, gl_element_init (plugin));
71 
72 static gboolean gst_gl_overlay_set_caps (GstGLFilter * filter,
73     GstCaps * incaps, GstCaps * outcaps);
74 
75 static void gst_gl_overlay_set_property (GObject * object, guint prop_id,
76     const GValue * value, GParamSpec * pspec);
77 static void gst_gl_overlay_get_property (GObject * object, guint prop_id,
78     GValue * value, GParamSpec * pspec);
79 
80 static void gst_gl_overlay_before_transform (GstBaseTransform * trans,
81     GstBuffer * outbuf);
82 static gboolean gst_gl_overlay_filter_texture (GstGLFilter * filter,
83     GstGLMemory * in_tex, GstGLMemory * out_tex);
84 
85 static gboolean gst_gl_overlay_load_png (GstGLOverlay * overlay, FILE * fp);
86 static gboolean gst_gl_overlay_load_jpeg (GstGLOverlay * overlay, FILE * fp);
87 
88 enum
89 {
90   PROP_0,
91   PROP_LOCATION,
92   PROP_OFFSET_X,
93   PROP_OFFSET_Y,
94   PROP_RELATIVE_X,
95   PROP_RELATIVE_Y,
96   PROP_OVERLAY_WIDTH,
97   PROP_OVERLAY_HEIGHT,
98   PROP_ALPHA
99 };
100 
101 /* *INDENT-OFF* */
102 /* vertex source */
103 static const gchar *overlay_v_src =
104     "attribute vec4 a_position;\n"
105     "attribute vec2 a_texcoord;\n"
106     "varying vec2 v_texcoord;\n"
107     "void main()\n"
108     "{\n"
109     "   gl_Position = a_position;\n"
110     "   v_texcoord = a_texcoord;\n"
111     "}";
112 
113 /* fragment source */
114 static const gchar *overlay_f_src =
115     "uniform sampler2D texture;\n"
116     "uniform float alpha;\n"
117     "varying vec2 v_texcoord;\n"
118     "void main()\n"
119     "{\n"
120     "  vec4 rgba = texture2D( texture, v_texcoord );\n"
121     "  gl_FragColor = vec4(rgba.rgb, rgba.a * alpha);\n"
122     "}\n";
123 /* *INDENT-ON* */
124 
125 /* init resources that need a gl context */
126 static gboolean
gst_gl_overlay_gl_start(GstGLBaseFilter * base_filter)127 gst_gl_overlay_gl_start (GstGLBaseFilter * base_filter)
128 {
129   GstGLOverlay *overlay = GST_GL_OVERLAY (base_filter);
130   gchar *frag_str;
131   gboolean ret;
132 
133   if (!GST_GL_BASE_FILTER_CLASS (parent_class)->gl_start (base_filter))
134     return FALSE;
135 
136   frag_str =
137       g_strdup_printf ("%s%s",
138       gst_gl_shader_string_get_highest_precision (base_filter->context,
139           GST_GLSL_VERSION_NONE,
140           GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY), overlay_f_src);
141 
142   /* blocking call, wait the opengl thread has compiled the shader */
143   ret = gst_gl_context_gen_shader (base_filter->context, overlay_v_src,
144       frag_str, &overlay->shader);
145   g_free (frag_str);
146 
147   return ret;
148 }
149 
150 /* free resources that need a gl context */
151 static void
gst_gl_overlay_gl_stop(GstGLBaseFilter * base_filter)152 gst_gl_overlay_gl_stop (GstGLBaseFilter * base_filter)
153 {
154   GstGLOverlay *overlay = GST_GL_OVERLAY (base_filter);
155   const GstGLFuncs *gl = base_filter->context->gl_vtable;
156 
157   if (overlay->shader) {
158     gst_object_unref (overlay->shader);
159     overlay->shader = NULL;
160   }
161 
162   if (overlay->image_memory) {
163     gst_memory_unref ((GstMemory *) overlay->image_memory);
164     overlay->image_memory = NULL;
165   }
166 
167   if (overlay->vao) {
168     gl->DeleteVertexArrays (1, &overlay->vao);
169     overlay->vao = 0;
170   }
171 
172   if (overlay->vbo) {
173     gl->DeleteBuffers (1, &overlay->vbo);
174     overlay->vbo = 0;
175   }
176 
177   if (overlay->vbo_indices) {
178     gl->DeleteBuffers (1, &overlay->vbo_indices);
179     overlay->vbo_indices = 0;
180   }
181 
182   if (overlay->overlay_vao) {
183     gl->DeleteVertexArrays (1, &overlay->overlay_vao);
184     overlay->overlay_vao = 0;
185   }
186 
187   if (overlay->overlay_vbo) {
188     gl->DeleteBuffers (1, &overlay->overlay_vbo);
189     overlay->overlay_vbo = 0;
190   }
191 
192   GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (base_filter);
193 }
194 
195 static void
gst_gl_overlay_class_init(GstGLOverlayClass * klass)196 gst_gl_overlay_class_init (GstGLOverlayClass * klass)
197 {
198   GObjectClass *gobject_class;
199   GstElementClass *element_class;
200 
201   gobject_class = (GObjectClass *) klass;
202   element_class = GST_ELEMENT_CLASS (klass);
203 
204   gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
205 
206   gobject_class->set_property = gst_gl_overlay_set_property;
207   gobject_class->get_property = gst_gl_overlay_get_property;
208 
209   GST_GL_BASE_FILTER_CLASS (klass)->gl_start = gst_gl_overlay_gl_start;
210   GST_GL_BASE_FILTER_CLASS (klass)->gl_stop = gst_gl_overlay_gl_stop;
211 
212   GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_overlay_set_caps;
213   GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_overlay_filter_texture;
214 
215   GST_BASE_TRANSFORM_CLASS (klass)->before_transform =
216       GST_DEBUG_FUNCPTR (gst_gl_overlay_before_transform);
217 
218   g_object_class_install_property (gobject_class, PROP_LOCATION,
219       g_param_spec_string ("location", "location",
220           "Location of image file to overlay", NULL, GST_PARAM_CONTROLLABLE
221           | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
222           | G_PARAM_STATIC_STRINGS));
223   g_object_class_install_property (gobject_class, PROP_OFFSET_X,
224       g_param_spec_int ("offset-x", "X Offset",
225           "For positive value, horizontal offset of overlay image in pixels from"
226           " left of video image. For negative value, horizontal offset of overlay"
227           " image in pixels from right of video image", G_MININT, G_MAXINT, 0,
228           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
229           | G_PARAM_STATIC_STRINGS));
230   g_object_class_install_property (gobject_class, PROP_OFFSET_Y,
231       g_param_spec_int ("offset-y", "Y Offset",
232           "For positive value, vertical offset of overlay image in pixels from"
233           " top of video image. For negative value, vertical offset of overlay"
234           " image in pixels from bottom of video image", G_MININT, G_MAXINT, 0,
235           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
236           | G_PARAM_STATIC_STRINGS));
237   g_object_class_install_property (gobject_class, PROP_RELATIVE_X,
238       g_param_spec_double ("relative-x", "Relative X Offset",
239           "Horizontal offset of overlay image in fractions of video image "
240           "width, from top-left corner of video image", 0.0, 1.0, 0.0,
241           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
242           | G_PARAM_STATIC_STRINGS));
243   g_object_class_install_property (gobject_class, PROP_RELATIVE_Y,
244       g_param_spec_double ("relative-y", "Relative Y Offset",
245           "Vertical offset of overlay image in fractions of video image "
246           "height, from top-left corner of video image", 0.0, 1.0, 0.0,
247           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
248           | G_PARAM_STATIC_STRINGS));
249   g_object_class_install_property (gobject_class, PROP_OVERLAY_WIDTH,
250       g_param_spec_int ("overlay-width", "Overlay Width",
251           "Width of overlay image in pixels (0 = same as overlay image)", 0,
252           G_MAXINT, 0,
253           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
254           | G_PARAM_STATIC_STRINGS));
255   g_object_class_install_property (gobject_class, PROP_OVERLAY_HEIGHT,
256       g_param_spec_int ("overlay-height", "Overlay Height",
257           "Height of overlay image in pixels (0 = same as overlay image)", 0,
258           G_MAXINT, 0,
259           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
260           | G_PARAM_STATIC_STRINGS));
261   g_object_class_install_property (gobject_class, PROP_ALPHA,
262       g_param_spec_double ("alpha", "Alpha", "Global alpha of overlay image",
263           0.0, 1.0, 1.0, GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING
264           | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265 
266   gst_element_class_set_metadata (element_class,
267       "Gstreamer OpenGL Overlay", "Filter/Effect/Video",
268       "Overlay GL video texture with a JPEG/PNG image",
269       "Filippo Argiolas <filippo.argiolas@gmail.com>, "
270       "Matthew Waters <matthew@centricular.com>");
271 
272   GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
273       GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
274 }
275 
276 static void
gst_gl_overlay_init(GstGLOverlay * overlay)277 gst_gl_overlay_init (GstGLOverlay * overlay)
278 {
279   overlay->offset_x = 0;
280   overlay->offset_y = 0;
281 
282   overlay->relative_x = 0.0;
283   overlay->relative_y = 0.0;
284 
285   overlay->overlay_width = 0;
286   overlay->overlay_height = 0;
287 
288   overlay->alpha = 1.0;
289 }
290 
291 static void
gst_gl_overlay_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)292 gst_gl_overlay_set_property (GObject * object, guint prop_id,
293     const GValue * value, GParamSpec * pspec)
294 {
295   GstGLOverlay *overlay = GST_GL_OVERLAY (object);
296 
297   switch (prop_id) {
298     case PROP_LOCATION:
299       g_free (overlay->location);
300       overlay->location_has_changed = TRUE;
301       overlay->location = g_value_dup_string (value);
302       break;
303     case PROP_OFFSET_X:
304       overlay->offset_x = g_value_get_int (value);
305       overlay->geometry_change = TRUE;
306       break;
307     case PROP_OFFSET_Y:
308       overlay->offset_y = g_value_get_int (value);
309       overlay->geometry_change = TRUE;
310       break;
311     case PROP_RELATIVE_X:
312       overlay->relative_x = g_value_get_double (value);
313       overlay->geometry_change = TRUE;
314       break;
315     case PROP_RELATIVE_Y:
316       overlay->relative_y = g_value_get_double (value);
317       overlay->geometry_change = TRUE;
318       break;
319     case PROP_OVERLAY_WIDTH:
320       overlay->overlay_width = g_value_get_int (value);
321       overlay->geometry_change = TRUE;
322       break;
323     case PROP_OVERLAY_HEIGHT:
324       overlay->overlay_height = g_value_get_int (value);
325       overlay->geometry_change = TRUE;
326       break;
327     case PROP_ALPHA:
328       overlay->alpha = g_value_get_double (value);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333   }
334 }
335 
336 static void
gst_gl_overlay_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)337 gst_gl_overlay_get_property (GObject * object, guint prop_id,
338     GValue * value, GParamSpec * pspec)
339 {
340   GstGLOverlay *overlay = GST_GL_OVERLAY (object);
341 
342   switch (prop_id) {
343     case PROP_LOCATION:
344       g_value_set_string (value, overlay->location);
345       break;
346     case PROP_OFFSET_X:
347       g_value_set_int (value, overlay->offset_x);
348       break;
349     case PROP_OFFSET_Y:
350       g_value_set_int (value, overlay->offset_y);
351       break;
352     case PROP_RELATIVE_X:
353       g_value_set_double (value, overlay->relative_x);
354       break;
355     case PROP_RELATIVE_Y:
356       g_value_set_double (value, overlay->relative_y);
357       break;
358     case PROP_OVERLAY_WIDTH:
359       g_value_set_int (value, overlay->overlay_width);
360       break;
361     case PROP_OVERLAY_HEIGHT:
362       g_value_set_int (value, overlay->overlay_height);
363       break;
364     case PROP_ALPHA:
365       g_value_set_double (value, overlay->alpha);
366       break;
367     default:
368       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
369       break;
370   }
371 }
372 
373 static gboolean
gst_gl_overlay_set_caps(GstGLFilter * filter,GstCaps * incaps,GstCaps * outcaps)374 gst_gl_overlay_set_caps (GstGLFilter * filter, GstCaps * incaps,
375     GstCaps * outcaps)
376 {
377   GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
378   GstStructure *s = gst_caps_get_structure (incaps, 0);
379   gint width = 0;
380   gint height = 0;
381 
382   gst_structure_get_int (s, "width", &width);
383   gst_structure_get_int (s, "height", &height);
384 
385   overlay->window_width = width;
386   overlay->window_height = height;
387 
388   return TRUE;
389 }
390 
391 static void
_unbind_buffer(GstGLOverlay * overlay)392 _unbind_buffer (GstGLOverlay * overlay)
393 {
394   GstGLFilter *filter = GST_GL_FILTER (overlay);
395   const GstGLFuncs *gl = GST_GL_BASE_FILTER (overlay)->context->gl_vtable;
396 
397   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
398   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
399 
400   gl->DisableVertexAttribArray (filter->draw_attr_position_loc);
401   gl->DisableVertexAttribArray (filter->draw_attr_texture_loc);
402 }
403 
404 static void
_bind_buffer(GstGLOverlay * overlay,GLuint vbo)405 _bind_buffer (GstGLOverlay * overlay, GLuint vbo)
406 {
407   GstGLFilter *filter = GST_GL_FILTER (overlay);
408   const GstGLFuncs *gl = GST_GL_BASE_FILTER (overlay)->context->gl_vtable;
409 
410   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->vbo_indices);
411   gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
412 
413   gl->EnableVertexAttribArray (filter->draw_attr_position_loc);
414   gl->EnableVertexAttribArray (filter->draw_attr_texture_loc);
415 
416   gl->VertexAttribPointer (filter->draw_attr_position_loc, 3, GL_FLOAT,
417       GL_FALSE, 5 * sizeof (GLfloat), (void *) 0);
418   gl->VertexAttribPointer (filter->draw_attr_texture_loc, 2, GL_FLOAT,
419       GL_FALSE, 5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
420 }
421 
422 /* *INDENT-OFF* */
423 float v_vertices[] = {
424 /*|      Vertex     | TexCoord  |*/
425   -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
426    1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
427    1.0f,  1.0f, 0.0f, 1.0f, 1.0f,
428   -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,
429 };
430 
431 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3, };
432 /* *INDENT-ON* */
433 
434 static gboolean
gst_gl_overlay_callback(GstGLFilter * filter,GstGLMemory * in_tex,gpointer stuff)435 gst_gl_overlay_callback (GstGLFilter * filter, GstGLMemory * in_tex,
436     gpointer stuff)
437 {
438   GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
439   GstMapInfo map_info;
440   guint image_tex;
441   gboolean memory_mapped = FALSE;
442   const GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
443   gboolean ret = FALSE;
444 
445 #if GST_GL_HAVE_OPENGL
446   if (gst_gl_context_get_gl_api (GST_GL_BASE_FILTER (filter)->context) &
447       GST_GL_API_OPENGL) {
448 
449     gl->MatrixMode (GL_PROJECTION);
450     gl->LoadIdentity ();
451   }
452 #endif
453 
454   gl->ActiveTexture (GL_TEXTURE0);
455   gl->BindTexture (GL_TEXTURE_2D, gst_gl_memory_get_texture_id (in_tex));
456 
457   gst_gl_shader_use (overlay->shader);
458 
459   gst_gl_shader_set_uniform_1f (overlay->shader, "alpha", 1.0f);
460   gst_gl_shader_set_uniform_1i (overlay->shader, "texture", 0);
461 
462   filter->draw_attr_position_loc =
463       gst_gl_shader_get_attribute_location (overlay->shader, "a_position");
464   filter->draw_attr_texture_loc =
465       gst_gl_shader_get_attribute_location (overlay->shader, "a_texcoord");
466 
467   gst_gl_filter_draw_fullscreen_quad (filter);
468 
469   if (!overlay->image_memory)
470     goto out;
471 
472   if (!gst_memory_map ((GstMemory *) overlay->image_memory, &map_info,
473           GST_MAP_READ | GST_MAP_GL) || map_info.data == NULL)
474     goto out;
475 
476   memory_mapped = TRUE;
477   image_tex = *(guint *) map_info.data;
478 
479   if (!overlay->overlay_vbo) {
480     if (gl->GenVertexArrays) {
481       gl->GenVertexArrays (1, &overlay->overlay_vao);
482       gl->BindVertexArray (overlay->overlay_vao);
483     }
484 
485     gl->GenBuffers (1, &overlay->vbo_indices);
486     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->vbo_indices);
487     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
488         GL_STATIC_DRAW);
489 
490     gl->GenBuffers (1, &overlay->overlay_vbo);
491     gl->BindBuffer (GL_ARRAY_BUFFER, overlay->overlay_vbo);
492     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->vbo_indices);
493     overlay->geometry_change = TRUE;
494   }
495 
496   if (gl->GenVertexArrays) {
497     gl->BindVertexArray (overlay->overlay_vao);
498   }
499 
500   if (overlay->geometry_change) {
501     gint render_width, render_height;
502     gfloat x, y, image_width, image_height;
503 
504     /* *INDENT-OFF* */
505     float vertices[] = {
506      -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
507       1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
508       1.0f,  1.0f, 0.0f, 1.0f, 1.0f,
509      -1.0f,  1.0f, 0.0f, 0.0,  1.0f,
510     };
511     /* *INDENT-ON* */
512 
513     /* scale from [0, 1] -> [-1, 1] */
514     x = ((gfloat) overlay->offset_x / (gfloat) overlay->window_width +
515         overlay->relative_x) * 2.0f - 1.0;
516     y = ((gfloat) overlay->offset_y / (gfloat) overlay->window_height +
517         overlay->relative_y) * 2.0f - 1.0;
518     /* scale from [0, 1] -> [0, 2] */
519     render_width =
520         overlay->overlay_width >
521         0 ? overlay->overlay_width : overlay->image_width;
522     render_height =
523         overlay->overlay_height >
524         0 ? overlay->overlay_height : overlay->image_height;
525     image_width =
526         ((gfloat) render_width / (gfloat) overlay->window_width) * 2.0f;
527     image_height =
528         ((gfloat) render_height / (gfloat) overlay->window_height) * 2.0f;
529 
530     vertices[0] = vertices[15] = x;
531     vertices[5] = vertices[10] = x + image_width;
532     vertices[1] = vertices[6] = y;
533     vertices[11] = vertices[16] = y + image_height;
534 
535     gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
536         GL_STATIC_DRAW);
537   }
538 
539   _bind_buffer (overlay, overlay->overlay_vbo);
540 
541   gl->BindTexture (GL_TEXTURE_2D, image_tex);
542   gst_gl_shader_set_uniform_1f (overlay->shader, "alpha", overlay->alpha);
543 
544   gl->Enable (GL_BLEND);
545   if (gl->BlendFuncSeparate)
546     gl->BlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
547         GL_ONE_MINUS_SRC_ALPHA);
548   else
549     gl->BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
550   gl->BlendEquation (GL_FUNC_ADD);
551 
552   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
553 
554   gl->Disable (GL_BLEND);
555   ret = TRUE;
556 
557 out:
558   if (gl->GenVertexArrays)
559     gl->BindVertexArray (0);
560   else
561     _unbind_buffer (overlay);
562 
563   gst_gl_context_clear_shader (GST_GL_BASE_FILTER (filter)->context);
564 
565   if (memory_mapped)
566     gst_memory_unmap ((GstMemory *) overlay->image_memory, &map_info);
567 
568   overlay->geometry_change = FALSE;
569 
570   return ret;
571 }
572 
573 static gboolean
load_file(GstGLOverlay * overlay)574 load_file (GstGLOverlay * overlay)
575 {
576   FILE *fp;
577   guint8 buff[16];
578   gsize n_read;
579   GstCaps *caps;
580   GstStructure *structure;
581   gboolean success = FALSE;
582 
583   if (overlay->location == NULL)
584     return TRUE;
585 
586   if ((fp = fopen (overlay->location, "rb")) == NULL) {
587     GST_ELEMENT_ERROR (overlay, RESOURCE, NOT_FOUND, ("Can't open file"),
588         ("File: %s", overlay->location));
589     return FALSE;
590   }
591 
592   n_read = fread (buff, 1, sizeof (buff), fp);
593   if (n_read != sizeof (buff)) {
594     GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("Can't read file header"),
595         ("File: %s", overlay->location));
596     goto out;
597   }
598 
599   caps = gst_type_find_helper_for_data (GST_OBJECT (overlay), buff,
600       sizeof (buff), NULL);
601 
602   if (caps == NULL) {
603     GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("Can't find file type"),
604         ("File: %s", overlay->location));
605     goto out;
606   }
607 
608   fseek (fp, 0, SEEK_SET);
609 
610   structure = gst_caps_get_structure (caps, 0);
611   if (gst_structure_has_name (structure, "image/jpeg")) {
612     success = gst_gl_overlay_load_jpeg (overlay, fp);
613   } else if (gst_structure_has_name (structure, "image/png")) {
614     success = gst_gl_overlay_load_png (overlay, fp);
615   } else {
616     GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("Image type not supported"),
617         ("File: %s", overlay->location));
618   }
619 
620 out:
621   fclose (fp);
622   gst_caps_replace (&caps, NULL);
623 
624   return success;
625 }
626 
627 static gboolean
gst_gl_overlay_filter_texture(GstGLFilter * filter,GstGLMemory * in_tex,GstGLMemory * out_tex)628 gst_gl_overlay_filter_texture (GstGLFilter * filter, GstGLMemory * in_tex,
629     GstGLMemory * out_tex)
630 {
631   GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
632 
633   if (overlay->location_has_changed) {
634     if (overlay->image_memory) {
635       gst_memory_unref ((GstMemory *) overlay->image_memory);
636       overlay->image_memory = NULL;
637     }
638 
639     if (!load_file (overlay))
640       return FALSE;
641 
642     overlay->location_has_changed = FALSE;
643   }
644 
645   gst_gl_filter_render_to_target (filter, in_tex, out_tex,
646       gst_gl_overlay_callback, overlay);
647 
648   return TRUE;
649 }
650 
651 static void
gst_gl_overlay_before_transform(GstBaseTransform * trans,GstBuffer * outbuf)652 gst_gl_overlay_before_transform (GstBaseTransform * trans, GstBuffer * outbuf)
653 {
654   GstClockTime stream_time;
655 
656   stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
657       GST_BUFFER_TIMESTAMP (outbuf));
658 
659   if (GST_CLOCK_TIME_IS_VALID (stream_time))
660     gst_object_sync_values (GST_OBJECT (trans), stream_time);
661 }
662 
663 static void
user_warning_fn(png_structp png_ptr,png_const_charp warning_msg)664 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
665 {
666   g_warning ("%s\n", warning_msg);
667 }
668 
669 static gboolean
gst_gl_overlay_load_jpeg(GstGLOverlay * overlay,FILE * fp)670 gst_gl_overlay_load_jpeg (GstGLOverlay * overlay, FILE * fp)
671 {
672   GstGLBaseMemoryAllocator *mem_allocator;
673   GstGLVideoAllocationParams *params;
674   GstVideoInfo v_info;
675   GstVideoAlignment v_align;
676   GstMapInfo map_info;
677   struct jpeg_decompress_struct cinfo;
678   struct jpeg_error_mgr jerr;
679   JSAMPROW j;
680   int i;
681 
682   jpeg_create_decompress (&cinfo);
683   cinfo.err = jpeg_std_error (&jerr);
684   jpeg_stdio_src (&cinfo, fp);
685   jpeg_read_header (&cinfo, TRUE);
686   jpeg_start_decompress (&cinfo);
687   overlay->image_width = cinfo.image_width;
688   overlay->image_height = cinfo.image_height;
689 
690   if (cinfo.num_components == 1)
691     gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_Y444,
692         overlay->image_width, overlay->image_height);
693   else
694     gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGB,
695         overlay->image_width, overlay->image_height);
696 
697   gst_video_alignment_reset (&v_align);
698   v_align.stride_align[0] = 32 - 1;
699   gst_video_info_align (&v_info, &v_align);
700 
701   mem_allocator =
702       GST_GL_BASE_MEMORY_ALLOCATOR (gst_gl_memory_allocator_get_default
703       (GST_GL_BASE_FILTER (overlay)->context));
704   params =
705       gst_gl_video_allocation_params_new (GST_GL_BASE_FILTER (overlay)->context,
706       NULL, &v_info, 0, &v_align, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA);
707   overlay->image_memory = (GstGLMemory *)
708       gst_gl_base_memory_alloc (mem_allocator,
709       (GstGLAllocationParams *) params);
710   gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
711   gst_object_unref (mem_allocator);
712 
713   if (!gst_memory_map ((GstMemory *) overlay->image_memory, &map_info,
714           GST_MAP_WRITE)) {
715     GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("failed to map memory"),
716         ("File: %s", overlay->location));
717     return FALSE;
718   }
719 
720   for (i = 0; i < overlay->image_height; ++i) {
721     j = map_info.data + v_info.stride[0] * i;
722     jpeg_read_scanlines (&cinfo, &j, 1);
723   }
724   jpeg_finish_decompress (&cinfo);
725   jpeg_destroy_decompress (&cinfo);
726   gst_memory_unmap ((GstMemory *) overlay->image_memory, &map_info);
727 
728   return TRUE;
729 }
730 
731 static gboolean
gst_gl_overlay_load_png(GstGLOverlay * overlay,FILE * fp)732 gst_gl_overlay_load_png (GstGLOverlay * overlay, FILE * fp)
733 {
734   GstGLBaseMemoryAllocator *mem_allocator;
735   GstGLVideoAllocationParams *params;
736   GstVideoInfo v_info;
737   GstMapInfo map_info;
738 
739   png_structp png_ptr;
740   png_infop info_ptr;
741   png_uint_32 width = 0;
742   png_uint_32 height = 0;
743   gint bit_depth = 0;
744   gint color_type = 0;
745   gint interlace_type = 0;
746   guint y = 0;
747   guchar **rows = NULL;
748   gint filler;
749   png_byte magic[8];
750   gint n_read;
751 
752   if (!GST_GL_BASE_FILTER (overlay)->context)
753     return FALSE;
754 
755   /* Read magic number */
756   n_read = fread (magic, 1, sizeof (magic), fp);
757   if (n_read != sizeof (magic)) {
758     GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
759         ("can't read PNG magic number"), ("File: %s", overlay->location));
760     return FALSE;
761   }
762 
763   /* Check for valid magic number */
764   if (png_sig_cmp (magic, 0, sizeof (magic))) {
765     GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
766         ("not a valid PNG image"), ("File: %s", overlay->location));
767     return FALSE;
768   }
769 
770   png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
771 
772   if (png_ptr == NULL) {
773     GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
774         ("failed to initialize the png_struct"), ("File: %s",
775             overlay->location));
776     return FALSE;
777   }
778 
779   png_set_error_fn (png_ptr, NULL, NULL, user_warning_fn);
780 
781   info_ptr = png_create_info_struct (png_ptr);
782   if (info_ptr == NULL) {
783     png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL);
784     GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
785         ("failed to initialize the memory for image information"),
786         ("File: %s", overlay->location));
787     return FALSE;
788   }
789 
790   png_init_io (png_ptr, fp);
791 
792   png_set_sig_bytes (png_ptr, sizeof (magic));
793 
794   png_read_info (png_ptr, info_ptr);
795 
796   png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
797       &interlace_type, int_p_NULL, int_p_NULL);
798 
799   if (color_type == PNG_COLOR_TYPE_RGB) {
800     filler = 0xff;
801     png_set_filler (png_ptr, filler, PNG_FILLER_AFTER);
802     color_type = PNG_COLOR_TYPE_RGB_ALPHA;
803   }
804 
805   if (color_type != PNG_COLOR_TYPE_RGB_ALPHA) {
806     png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL);
807     GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
808         ("color type is not rgb"), ("File: %s", overlay->location));
809     return FALSE;
810   }
811 
812   overlay->image_width = width;
813   overlay->image_height = height;
814 
815   gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, width, height);
816   mem_allocator =
817       GST_GL_BASE_MEMORY_ALLOCATOR (gst_gl_memory_allocator_get_default
818       (GST_GL_BASE_FILTER (overlay)->context));
819   params =
820       gst_gl_video_allocation_params_new (GST_GL_BASE_FILTER (overlay)->context,
821       NULL, &v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA);
822   overlay->image_memory = (GstGLMemory *)
823       gst_gl_base_memory_alloc (mem_allocator,
824       (GstGLAllocationParams *) params);
825   gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
826   gst_object_unref (mem_allocator);
827 
828   if (!gst_memory_map ((GstMemory *) overlay->image_memory, &map_info,
829           GST_MAP_WRITE)) {
830     png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL);
831     GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
832         ("failed to map memory"), ("File: %s", overlay->location));
833     return FALSE;
834   }
835   rows = (guchar **) malloc (sizeof (guchar *) * height);
836 
837   for (y = 0; y < height; ++y)
838     rows[y] = (guchar *) (map_info.data + y * width * 4);
839 
840   png_read_image (png_ptr, rows);
841 
842   free (rows);
843   gst_memory_unmap ((GstMemory *) overlay->image_memory, &map_info);
844 
845   png_read_end (png_ptr, info_ptr);
846   png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL);
847 
848   return TRUE;
849 }
850