gstreamer/ext/gl/gstgloverlay.c
Alessandro Decina 28dbe4fffc glmemory: add gst_gl_memory_allocator_get_default
Add gst_gl_memory_allocator_get_default to get the default allocator based on
the opengl version. Allows us to stop hardcoding the PBO allocator which isn't
supported on gles2.

Fixes GL upload on iOS9 among other things.
2017-12-09 19:32:13 +00:00

848 lines
26 KiB
C

/*
* GStreamer
* Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-gloverlay
*
* Overlay GL video texture with a PNG image
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 videotestsrc ! gloverlay location=image.jpg ! glimagesink
* ]|
* FBO (Frame Buffer Object) is required.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/base/gsttypefindhelper.h>
#include <gst/gl/gstglconfig.h>
#include "gstgloverlay.h"
#include "effects/gstgleffectssources.h"
#include <stdio.h>
#include <stdlib.h>
#include <jpeglib.h>
#include <png.h>
#if PNG_LIBPNG_VER >= 10400
#define int_p_NULL NULL
#define png_infopp_NULL NULL
#endif
#define GST_CAT_DEFAULT gst_gl_overlay_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define DEBUG_INIT \
GST_DEBUG_CATEGORY_INIT (gst_gl_overlay_debug, "gloverlay", 0, "gloverlay element");
G_DEFINE_TYPE_WITH_CODE (GstGLOverlay, gst_gl_overlay, GST_TYPE_GL_FILTER,
DEBUG_INIT);
static gboolean gst_gl_overlay_set_caps (GstGLFilter * filter,
GstCaps * incaps, GstCaps * outcaps);
static void gst_gl_overlay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_gl_overlay_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_gl_overlay_before_transform (GstBaseTransform * trans,
GstBuffer * outbuf);
static gboolean gst_gl_overlay_filter_texture (GstGLFilter * filter,
guint in_tex, guint out_tex);
static gboolean gst_gl_overlay_load_png (GstGLOverlay * overlay, FILE * fp);
static gboolean gst_gl_overlay_load_jpeg (GstGLOverlay * overlay, FILE * fp);
enum
{
PROP_0,
PROP_LOCATION,
PROP_OFFSET_X,
PROP_OFFSET_Y,
PROP_RELATIVE_X,
PROP_RELATIVE_Y,
PROP_OVERLAY_WIDTH,
PROP_OVERLAY_HEIGHT,
PROP_ALPHA
};
/* *INDENT-OFF* */
/* vertex source */
static const gchar *overlay_v_src =
"attribute vec4 a_position;\n"
"attribute vec2 a_texcoord;\n"
"varying vec2 v_texcoord;\n"
"void main()\n"
"{\n"
" gl_Position = a_position;\n"
" v_texcoord = a_texcoord;\n"
"}";
/* fragment source */
static const gchar *overlay_f_src =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"uniform sampler2D texture;\n"
"uniform float alpha;\n"
"varying vec2 v_texcoord;\n"
"void main()\n"
"{\n"
" vec4 rgba = texture2D( texture, v_texcoord );\n"
" gl_FragColor = vec4(rgba.rgb, rgba.a * alpha);\n"
"}\n";
/* *INDENT-ON* */
/* init resources that need a gl context */
static gboolean
gst_gl_overlay_init_gl_resources (GstGLFilter * filter)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
if (overlay->shader)
gst_gl_context_del_shader (GST_GL_BASE_FILTER (filter)->context,
overlay->shader);
return gst_gl_context_gen_shader (GST_GL_BASE_FILTER (filter)->context,
overlay_v_src, overlay_f_src, &overlay->shader);
}
/* free resources that need a gl context */
static void
gst_gl_overlay_reset_gl_resources (GstGLFilter * filter)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
const GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
if (overlay->shader) {
gst_gl_context_del_shader (GST_GL_BASE_FILTER (filter)->context,
overlay->shader);
overlay->shader = NULL;
}
if (overlay->image_memory) {
gst_memory_unref ((GstMemory *) overlay->image_memory);
overlay->image_memory = NULL;
}
if (overlay->vao) {
gl->DeleteVertexArrays (1, &overlay->vao);
overlay->vao = 0;
}
if (overlay->vbo) {
gl->DeleteBuffers (1, &overlay->vbo);
overlay->vbo = 0;
}
if (overlay->vbo_indices) {
gl->DeleteBuffers (1, &overlay->vbo_indices);
overlay->vbo_indices = 0;
}
if (overlay->overlay_vao) {
gl->DeleteVertexArrays (1, &overlay->overlay_vao);
overlay->overlay_vao = 0;
}
if (overlay->overlay_vbo) {
gl->DeleteBuffers (1, &overlay->overlay_vbo);
overlay->overlay_vbo = 0;
}
}
static void
gst_gl_overlay_class_init (GstGLOverlayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
gobject_class = (GObjectClass *) klass;
element_class = GST_ELEMENT_CLASS (klass);
gobject_class->set_property = gst_gl_overlay_set_property;
gobject_class->get_property = gst_gl_overlay_get_property;
GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_overlay_set_caps;
GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_overlay_filter_texture;
GST_GL_FILTER_CLASS (klass)->display_reset_cb =
gst_gl_overlay_reset_gl_resources;
GST_GL_FILTER_CLASS (klass)->init_fbo = gst_gl_overlay_init_gl_resources;
GST_BASE_TRANSFORM_CLASS (klass)->before_transform =
GST_DEBUG_FUNCPTR (gst_gl_overlay_before_transform);
g_object_class_install_property (gobject_class, PROP_LOCATION,
g_param_spec_string ("location", "location",
"Location of image file to overlay", NULL, GST_PARAM_CONTROLLABLE
| GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_OFFSET_X,
g_param_spec_int ("offset-x", "X Offset",
"For positive value, horizontal offset of overlay image in pixels from"
" left of video image. For negative value, horizontal offset of overlay"
" image in pixels from right of video image", G_MININT, G_MAXINT, 0,
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_OFFSET_Y,
g_param_spec_int ("offset-y", "Y Offset",
"For positive value, vertical offset of overlay image in pixels from"
" top of video image. For negative value, vertical offset of overlay"
" image in pixels from bottom of video image", G_MININT, G_MAXINT, 0,
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_RELATIVE_X,
g_param_spec_double ("relative-x", "Relative X Offset",
"Horizontal offset of overlay image in fractions of video image "
"width, from top-left corner of video image", 0.0, 1.0, 0.0,
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_RELATIVE_Y,
g_param_spec_double ("relative-y", "Relative Y Offset",
"Vertical offset of overlay image in fractions of video image "
"height, from top-left corner of video image", 0.0, 1.0, 0.0,
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_OVERLAY_WIDTH,
g_param_spec_int ("overlay-width", "Overlay Width",
"Width of overlay image in pixels (0 = same as overlay image)", 0,
G_MAXINT, 0,
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_OVERLAY_HEIGHT,
g_param_spec_int ("overlay-height", "Overlay Height",
"Height of overlay image in pixels (0 = same as overlay image)", 0,
G_MAXINT, 0,
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_ALPHA,
g_param_spec_double ("alpha", "Alpha", "Global alpha of overlay image",
0.0, 1.0, 1.0, GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_set_metadata (element_class,
"Gstreamer OpenGL Overlay", "Filter/Effect/Video",
"Overlay GL video texture with a JPEG/PNG image",
"Filippo Argiolas <filippo.argiolas@gmail.com>, "
"Matthew Waters <matthew@centricular.com>");
GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
}
static void
gst_gl_overlay_init (GstGLOverlay * overlay)
{
overlay->offset_x = 0;
overlay->offset_y = 0;
overlay->relative_x = 0.0;
overlay->relative_y = 0.0;
overlay->overlay_width = 0;
overlay->overlay_height = 0;
overlay->alpha = 1.0;
}
static void
gst_gl_overlay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (object);
switch (prop_id) {
case PROP_LOCATION:
g_free (overlay->location);
overlay->location_has_changed = TRUE;
overlay->location = g_value_dup_string (value);
break;
case PROP_OFFSET_X:
overlay->offset_x = g_value_get_int (value);
overlay->geometry_change = TRUE;
break;
case PROP_OFFSET_Y:
overlay->offset_y = g_value_get_int (value);
overlay->geometry_change = TRUE;
break;
case PROP_RELATIVE_X:
overlay->relative_x = g_value_get_double (value);
overlay->geometry_change = TRUE;
break;
case PROP_RELATIVE_Y:
overlay->relative_y = g_value_get_double (value);
overlay->geometry_change = TRUE;
break;
case PROP_OVERLAY_WIDTH:
overlay->overlay_width = g_value_get_int (value);
overlay->geometry_change = TRUE;
break;
case PROP_OVERLAY_HEIGHT:
overlay->overlay_height = g_value_get_int (value);
overlay->geometry_change = TRUE;
break;
case PROP_ALPHA:
overlay->alpha = g_value_get_double (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_gl_overlay_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (object);
switch (prop_id) {
case PROP_LOCATION:
g_value_set_string (value, overlay->location);
break;
case PROP_OFFSET_X:
g_value_set_int (value, overlay->offset_x);
break;
case PROP_OFFSET_Y:
g_value_set_int (value, overlay->offset_y);
break;
case PROP_RELATIVE_X:
g_value_set_double (value, overlay->relative_x);
break;
case PROP_RELATIVE_Y:
g_value_set_double (value, overlay->relative_y);
break;
case PROP_OVERLAY_WIDTH:
g_value_set_int (value, overlay->overlay_width);
break;
case PROP_OVERLAY_HEIGHT:
g_value_set_int (value, overlay->overlay_height);
break;
case PROP_ALPHA:
g_value_set_double (value, overlay->alpha);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gst_gl_overlay_set_caps (GstGLFilter * filter, GstCaps * incaps,
GstCaps * outcaps)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
GstStructure *s = gst_caps_get_structure (incaps, 0);
gint width = 0;
gint height = 0;
gst_structure_get_int (s, "width", &width);
gst_structure_get_int (s, "height", &height);
overlay->window_width = width;
overlay->window_height = height;
return TRUE;
}
static void
_unbind_buffer (GstGLOverlay * overlay)
{
const GstGLFuncs *gl = GST_GL_BASE_FILTER (overlay)->context->gl_vtable;
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->DisableVertexAttribArray (overlay->attr_position);
gl->DisableVertexAttribArray (overlay->attr_texture);
}
static void
_bind_buffer (GstGLOverlay * overlay, GLuint vbo)
{
const GstGLFuncs *gl = GST_GL_BASE_FILTER (overlay)->context->gl_vtable;
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->vbo_indices);
gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
gl->EnableVertexAttribArray (overlay->attr_position);
gl->EnableVertexAttribArray (overlay->attr_texture);
gl->VertexAttribPointer (overlay->attr_position, 3, GL_FLOAT,
GL_FALSE, 5 * sizeof (GLfloat), (void *) 0);
gl->VertexAttribPointer (overlay->attr_texture, 2, GL_FLOAT,
GL_FALSE, 5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
}
/* *INDENT-OFF* */
float v_vertices[] = {
/*| Vertex | TexCoord |*/
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
};
static const GLushort indices[] = { 0, 1, 2, 0, 2, 3, };
/* *INDENT-ON* */
static void
gst_gl_overlay_callback (gint width, gint height, guint texture, gpointer stuff)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (stuff);
GstGLFilter *filter = GST_GL_FILTER (overlay);
GstMapInfo map_info;
guint image_tex;
gboolean memory_mapped = FALSE;
const GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
#if GST_GL_HAVE_OPENGL
if (gst_gl_context_get_gl_api (GST_GL_BASE_FILTER (filter)->context) &
GST_GL_API_OPENGL) {
gl->MatrixMode (GL_PROJECTION);
gl->LoadIdentity ();
}
#endif
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (GL_TEXTURE_2D, texture);
gst_gl_shader_use (overlay->shader);
gst_gl_shader_set_uniform_1f (overlay->shader, "alpha", 1.0f);
gst_gl_shader_set_uniform_1i (overlay->shader, "texture", 0);
overlay->attr_position =
gst_gl_shader_get_attribute_location (overlay->shader, "a_position");
overlay->attr_texture =
gst_gl_shader_get_attribute_location (overlay->shader, "a_texcoord");
if (!overlay->vbo) {
if (gl->GenVertexArrays) {
gl->GenVertexArrays (1, &overlay->vao);
gl->BindVertexArray (overlay->vao);
}
gl->GenBuffers (1, &overlay->vbo);
gl->BindBuffer (GL_ARRAY_BUFFER, overlay->vbo);
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), v_vertices,
GL_STATIC_DRAW);
gl->GenBuffers (1, &overlay->vbo_indices);
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->vbo_indices);
gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
GL_STATIC_DRAW);
if (gl->GenVertexArrays) {
_bind_buffer (overlay, overlay->vbo);
gl->BindVertexArray (0);
}
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
}
if (gl->GenVertexArrays)
gl->BindVertexArray (overlay->vao);
else
_bind_buffer (overlay, overlay->vbo);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
if (!overlay->image_memory)
goto out;
if (!gst_memory_map ((GstMemory *) overlay->image_memory, &map_info,
GST_MAP_READ | GST_MAP_GL) || map_info.data == NULL)
goto out;
memory_mapped = TRUE;
image_tex = *(guint *) map_info.data;
if (!overlay->overlay_vbo) {
if (gl->GenVertexArrays) {
gl->GenVertexArrays (1, &overlay->overlay_vao);
gl->BindVertexArray (overlay->overlay_vao);
}
gl->GenBuffers (1, &overlay->overlay_vbo);
gl->BindBuffer (GL_ARRAY_BUFFER, overlay->overlay_vbo);
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->vbo_indices);
overlay->geometry_change = TRUE;
}
if (gl->GenVertexArrays) {
gl->BindVertexArray (overlay->overlay_vao);
}
if (overlay->geometry_change) {
gint render_width, render_height;
gfloat x, y, image_width, image_height;
/* *INDENT-OFF* */
float vertices[] = {
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0, 1.0f,
};
/* *INDENT-ON* */
/* scale from [0, 1] -> [-1, 1] */
x = ((gfloat) overlay->offset_x / (gfloat) overlay->window_width +
overlay->relative_x) * 2.0f - 1.0;
y = ((gfloat) overlay->offset_y / (gfloat) overlay->window_height +
overlay->relative_y) * 2.0f - 1.0;
/* scale from [0, 1] -> [0, 2] */
render_width =
overlay->overlay_width >
0 ? overlay->overlay_width : overlay->image_width;
render_height =
overlay->overlay_height >
0 ? overlay->overlay_height : overlay->image_height;
image_width =
((gfloat) render_width / (gfloat) overlay->window_width) * 2.0f;
image_height =
((gfloat) render_height / (gfloat) overlay->window_height) * 2.0f;
vertices[0] = vertices[15] = x;
vertices[5] = vertices[10] = x + image_width;
vertices[1] = vertices[6] = y;
vertices[11] = vertices[16] = y + image_height;
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
GL_STATIC_DRAW);
}
if (!gl->GenVertexArrays || overlay->geometry_change) {
_bind_buffer (overlay, overlay->overlay_vbo);
}
gl->BindTexture (GL_TEXTURE_2D, image_tex);
gst_gl_shader_set_uniform_1f (overlay->shader, "alpha", overlay->alpha);
gl->Enable (GL_BLEND);
gl->BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl->BlendEquation (GL_FUNC_ADD);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
gl->Disable (GL_BLEND);
out:
if (gl->GenVertexArrays) {
gl->BindVertexArray (0);
} else {
_unbind_buffer (overlay);
}
gst_gl_context_clear_shader (GST_GL_BASE_FILTER (filter)->context);
if (memory_mapped)
gst_memory_unmap ((GstMemory *) overlay->image_memory, &map_info);
overlay->geometry_change = FALSE;
}
static gboolean
load_file (GstGLOverlay * overlay)
{
FILE *fp;
guint8 buff[16];
gsize n_read;
GstCaps *caps;
GstStructure *structure;
gboolean success = FALSE;
if (overlay->location == NULL)
return TRUE;
if ((fp = fopen (overlay->location, "rb")) == NULL) {
GST_ELEMENT_ERROR (overlay, RESOURCE, NOT_FOUND, ("Can't open file"),
("File: %s", overlay->location));
return FALSE;
}
n_read = fread (buff, 1, sizeof (buff), fp);
if (n_read != sizeof (buff)) {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("Can't read file header"),
("File: %s", overlay->location));
goto out;
}
caps = gst_type_find_helper_for_data (GST_OBJECT (overlay), buff,
sizeof (buff), NULL);
if (caps == NULL) {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("Can't find file type"),
("File: %s", overlay->location));
goto out;
}
fseek (fp, 0, SEEK_SET);
structure = gst_caps_get_structure (caps, 0);
if (gst_structure_has_name (structure, "image/jpeg")) {
success = gst_gl_overlay_load_jpeg (overlay, fp);
} else if (gst_structure_has_name (structure, "image/png")) {
success = gst_gl_overlay_load_png (overlay, fp);
} else {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("Image type not supported"),
("File: %s", overlay->location));
}
out:
fclose (fp);
gst_caps_replace (&caps, NULL);
return success;
}
static gboolean
gst_gl_overlay_filter_texture (GstGLFilter * filter, guint in_tex,
guint out_tex)
{
GstGLOverlay *overlay = GST_GL_OVERLAY (filter);
if (overlay->location_has_changed) {
if (overlay->image_memory) {
gst_memory_unref ((GstMemory *) overlay->image_memory);
overlay->image_memory = NULL;
}
if (!load_file (overlay))
return FALSE;
overlay->location_has_changed = FALSE;
}
gst_gl_filter_render_to_target (filter, TRUE, in_tex, out_tex,
gst_gl_overlay_callback, overlay);
return TRUE;
}
static void
gst_gl_overlay_before_transform (GstBaseTransform * trans, GstBuffer * outbuf)
{
GstClockTime stream_time;
stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
GST_BUFFER_TIMESTAMP (outbuf));
if (GST_CLOCK_TIME_IS_VALID (stream_time))
gst_object_sync_values (GST_OBJECT (trans), stream_time);
}
static void
user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
{
g_warning ("%s\n", warning_msg);
}
static gboolean
gst_gl_overlay_load_jpeg (GstGLOverlay * overlay, FILE * fp)
{
GstGLBaseMemoryAllocator *mem_allocator;
GstGLVideoAllocationParams *params;
GstVideoInfo v_info;
GstVideoAlignment v_align;
GstMapInfo map_info;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW j;
int i;
jpeg_create_decompress (&cinfo);
cinfo.err = jpeg_std_error (&jerr);
jpeg_stdio_src (&cinfo, fp);
jpeg_read_header (&cinfo, TRUE);
jpeg_start_decompress (&cinfo);
overlay->image_width = cinfo.image_width;
overlay->image_height = cinfo.image_height;
if (cinfo.num_components == 1)
gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_Y444,
overlay->image_width, overlay->image_height);
else
gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGB,
overlay->image_width, overlay->image_height);
gst_video_alignment_reset (&v_align);
v_align.stride_align[0] = 32 - 1;
gst_video_info_align (&v_info, &v_align);
mem_allocator =
GST_GL_BASE_MEMORY_ALLOCATOR (gst_gl_memory_allocator_get_default
(GST_GL_BASE_FILTER (overlay)->context));
params =
gst_gl_video_allocation_params_new (GST_GL_BASE_FILTER (overlay)->context,
NULL, &v_info, 0, &v_align, GST_GL_TEXTURE_TARGET_2D);
overlay->image_memory = (GstGLMemory *)
gst_gl_base_memory_alloc (mem_allocator,
(GstGLAllocationParams *) params);
gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
gst_object_unref (mem_allocator);
if (!gst_memory_map ((GstMemory *) overlay->image_memory, &map_info,
GST_MAP_WRITE)) {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE, ("failed to map memory"),
("File: %s", overlay->location));
return FALSE;
}
for (i = 0; i < overlay->image_height; ++i) {
j = map_info.data + v_info.stride[0] * i;
jpeg_read_scanlines (&cinfo, &j, 1);
}
jpeg_finish_decompress (&cinfo);
jpeg_destroy_decompress (&cinfo);
gst_memory_unmap ((GstMemory *) overlay->image_memory, &map_info);
return TRUE;
}
static gboolean
gst_gl_overlay_load_png (GstGLOverlay * overlay, FILE * fp)
{
GstGLBaseMemoryAllocator *mem_allocator;
GstGLVideoAllocationParams *params;
GstVideoInfo v_info;
GstMapInfo map_info;
png_structp png_ptr;
png_infop info_ptr;
png_uint_32 width = 0;
png_uint_32 height = 0;
gint bit_depth = 0;
gint color_type = 0;
gint interlace_type = 0;
guint y = 0;
guchar **rows = NULL;
gint filler;
png_byte magic[8];
gint n_read;
if (!GST_GL_BASE_FILTER (overlay)->context)
return FALSE;
/* Read magic number */
n_read = fread (magic, 1, sizeof (magic), fp);
if (n_read != sizeof (magic)) {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
("can't read PNG magic number"), ("File: %s", overlay->location));
return FALSE;
}
/* Check for valid magic number */
if (png_sig_cmp (magic, 0, sizeof (magic))) {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
("not a valid PNG image"), ("File: %s", overlay->location));
return FALSE;
}
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
("failed to initialize the png_struct"), ("File: %s",
overlay->location));
return FALSE;
}
png_set_error_fn (png_ptr, NULL, NULL, user_warning_fn);
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL) {
png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL);
GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
("failed to initialize the memory for image information"),
("File: %s", overlay->location));
return FALSE;
}
png_init_io (png_ptr, fp);
png_set_sig_bytes (png_ptr, sizeof (magic));
png_read_info (png_ptr, info_ptr);
png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, int_p_NULL, int_p_NULL);
if (color_type == PNG_COLOR_TYPE_RGB) {
filler = 0xff;
png_set_filler (png_ptr, filler, PNG_FILLER_AFTER);
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
}
if (color_type != PNG_COLOR_TYPE_RGB_ALPHA) {
png_destroy_read_struct (&png_ptr, png_infopp_NULL, png_infopp_NULL);
GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
("color type is not rgb"), ("File: %s", overlay->location));
return FALSE;
}
overlay->image_width = width;
overlay->image_height = height;
gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, width, height);
mem_allocator =
GST_GL_BASE_MEMORY_ALLOCATOR (gst_gl_memory_allocator_get_default
(GST_GL_BASE_FILTER (overlay)->context));
params =
gst_gl_video_allocation_params_new (GST_GL_BASE_FILTER (overlay)->context,
NULL, &v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D);
overlay->image_memory = (GstGLMemory *)
gst_gl_base_memory_alloc (mem_allocator,
(GstGLAllocationParams *) params);
gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
gst_object_unref (mem_allocator);
if (!gst_memory_map ((GstMemory *) overlay->image_memory, &map_info,
GST_MAP_WRITE)) {
png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL);
GST_ELEMENT_ERROR (overlay, STREAM, DECODE,
("failed to map memory"), ("File: %s", overlay->location));
return FALSE;
}
rows = (guchar **) malloc (sizeof (guchar *) * height);
for (y = 0; y < height; ++y)
rows[y] = (guchar *) (map_info.data + y * width * 4);
png_read_image (png_ptr, rows);
free (rows);
gst_memory_unmap ((GstMemory *) overlay->image_memory, &map_info);
png_read_end (png_ptr, info_ptr);
png_destroy_read_struct (&png_ptr, &info_ptr, png_infopp_NULL);
return TRUE;
}