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