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