gstreamer/sys/androidmedia/gstamc2dtexturerenderer.c

438 lines
13 KiB
C

/*
* Copyright (C) 2014, Collabora Ltd.
* Author: Matthieu Bouron <matthieu.bouron@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstamc2dtexturerenderer.h"
/* *INDENT-OFF* */
static const gchar frag_COPY_OES[] =
"#extension GL_OES_EGL_image_external : require \n"
"precision mediump float; \n"
"varying vec2 v_texcoord; \n"
"uniform samplerExternalOES u_tex; \n"
"void main (void) \n"
"{ \n"
" vec4 t = texture2D(u_tex, v_texcoord); \n"
" gl_FragColor = vec4(t.rgb, 1.0); \n"
"}";
static const gchar vert_COPY_OES[] =
"attribute vec4 a_position; \n"
"attribute vec2 a_texcoord; \n"
"varying vec2 v_texcoord; \n"
"uniform mat4 u_transformation; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texcoord = (u_transformation * vec4(a_texcoord, 0, 1)).xy; \n"
"}";
/* *INDENT-ON* */
static void
_surface_texture_detach_from_gl_context (GstGLContext * context,
GstAmc2DTextureRenderer * renderer)
{
renderer->gl_context_result =
gst_amc_surface_texture_detach_from_gl_context (renderer->surface_texture,
&renderer->gl_context_error);
}
static gboolean
_surface_texture_detach_from_gl_context_perform (GstAmc2DTextureRenderer *
renderer, GError ** error)
{
renderer->gl_context_result = FALSE;
renderer->gl_context_error = NULL;
gst_gl_context_thread_add (renderer->context,
(GstGLContextThreadFunc) _surface_texture_detach_from_gl_context,
renderer);
*error = renderer->gl_context_error;
renderer->gl_context_error = NULL;
return renderer->gl_context_result;
}
static void
_gen_oes_texture (GstGLContext * context, guint * tex_id)
{
const GstGLFuncs *gl = context->gl_vtable;
GST_TRACE ("Generating OES texture");
gl->GenTextures (1, tex_id);
gl->BindTexture (GL_TEXTURE_EXTERNAL_OES, *tex_id);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
gl->BindTexture (GL_TEXTURE_EXTERNAL_OES, 0);
GST_LOG ("generated OES texture id:%d", *tex_id);
}
GstAmc2DTextureRenderer *
gst_amc_2d_texture_renderer_new (GstGLContext * context,
GstAmcSurfaceTexture * surface_texture, guint width, guint height)
{
GstAmc2DTextureRenderer *renderer;
g_return_val_if_fail (surface_texture != NULL, NULL);
renderer = g_new0 (GstAmc2DTextureRenderer, 1);
if (!renderer)
return NULL;
renderer->context = gst_object_ref (context);
renderer->surface_texture = g_object_ref (surface_texture);
gst_video_info_init (&renderer->info);
gst_video_info_set_format (&renderer->info,
GST_VIDEO_FORMAT_RGBA, width, height);
return renderer;
}
void
gst_amc_2d_texture_renderer_free (GstAmc2DTextureRenderer * renderer)
{
GError *error = NULL;
if (renderer->surface_texture) {
_surface_texture_detach_from_gl_context_perform (renderer, &error);
g_object_unref (renderer->surface_texture);
}
if (renderer->fbo || renderer->depth_buffer) {
gst_gl_context_del_fbo (renderer->context, renderer->fbo,
renderer->depth_buffer);
}
if (renderer->shader) {
gst_object_unref (renderer->shader);
}
if (renderer->oes_tex_id) {
gst_gl_context_del_texture (renderer->context, &renderer->oes_tex_id);
}
if (renderer->context) {
gst_object_unref (renderer->context);
}
g_free (renderer);
}
static gboolean
_2d_texture_renderer_init_fbo (GstAmc2DTextureRenderer * renderer)
{
const GstGLFuncs *gl;
GLuint fake_texture = 0;
guint out_width, out_height;
guint internal_format;
out_width = GST_VIDEO_INFO_WIDTH (&renderer->info);
out_height = GST_VIDEO_INFO_HEIGHT (&renderer->info);
internal_format =
gst_gl_sized_gl_format_from_gl_format_type (renderer->context, GL_RGBA,
GL_UNSIGNED_BYTE);
gl = renderer->context->gl_vtable;
if (!gl->GenFramebuffers) {
/* turn off the pipeline because Frame buffer object is a not present */
gst_gl_context_set_error (renderer->context,
"Context, EXT_framebuffer_object supported: no");
return FALSE;
}
GST_INFO ("Context, EXT_framebuffer_object supported: yes");
/* setup FBO */
gl->GenFramebuffers (1, &renderer->fbo);
gl->BindFramebuffer (GL_FRAMEBUFFER, renderer->fbo);
/* setup the render buffer for depth */
gl->GenRenderbuffers (1, &renderer->depth_buffer);
gl->BindRenderbuffer (GL_RENDERBUFFER, renderer->depth_buffer);
gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
out_width, out_height);
/* a fake texture is attached to the render FBO (cannot init without it) */
gl->GenTextures (1, &fake_texture);
gl->BindTexture (GL_TEXTURE_2D, fake_texture);
gl->TexImage2D (GL_TEXTURE_2D, 0, internal_format, out_width, out_height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
/* attach the texture to the FBO to renderer to */
gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, fake_texture, 0);
/* attach the depth render buffer to the FBO */
gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, renderer->depth_buffer);
if (!gst_gl_context_check_framebuffer_status (renderer->context)) {
gst_gl_context_set_error (renderer->context,
"GL framebuffer status incomplete");
return FALSE;
}
/* unbind the FBO */
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
gl->DeleteTextures (1, &fake_texture);
return TRUE;
}
static gboolean
_2d_texture_renderer_init (GstAmc2DTextureRenderer * renderer)
{
GstGLFuncs *gl;
gboolean res;
gl = renderer->context->gl_vtable;
if (renderer->initialized)
return TRUE;
if (!gl->CreateProgramObject && !gl->CreateProgram) {
gst_gl_context_set_error (renderer->context,
"Cannot perform conversion without OpenGL shaders");
return FALSE;
}
_gen_oes_texture (renderer->context, &renderer->oes_tex_id);
res =
gst_gl_context_gen_shader (renderer->context, vert_COPY_OES,
frag_COPY_OES, &renderer->shader);
if (!res)
return FALSE;
renderer->shader_attr_position_loc =
gst_gl_shader_get_attribute_location (renderer->shader, "a_position");
renderer->shader_attr_texture_loc =
gst_gl_shader_get_attribute_location (renderer->shader, "a_texcoord");
gst_gl_shader_use (renderer->shader);
gst_gl_shader_set_uniform_1i (renderer->shader, "u_tex", 0);
gst_gl_context_clear_shader (renderer->context);
if (!_2d_texture_renderer_init_fbo (renderer))
return FALSE;
gl->BindTexture (GL_TEXTURE_2D, 0);
renderer->initialized = TRUE;
return TRUE;
}
static gboolean
_2d_texture_renderer_draw (GstAmc2DTextureRenderer * renderer)
{
GstGLFuncs *gl;
guint out_width, out_height;
GLint viewport_dim[4];
/* *INDENT-OFF* */
const GLfloat vertices[] = {
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f
};
/* *INDENT-ON* */
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
GLenum multipleRT[] = {
GL_COLOR_ATTACHMENT0,
};
gl = renderer->context->gl_vtable;
out_width = GST_VIDEO_INFO_WIDTH (&renderer->info);
out_height = GST_VIDEO_INFO_HEIGHT (&renderer->info);
gl->BindFramebuffer (GL_FRAMEBUFFER, renderer->fbo);
/* attach the texture to the FBO to rendererer to */
gl->BindTexture (GL_TEXTURE_2D, renderer->tex_id);
gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, renderer->tex_id, 0);
if (gl->DrawBuffers)
gl->DrawBuffers (1, multipleRT);
else if (gl->DrawBuffer)
gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
gl->GetIntegerv (GL_VIEWPORT, viewport_dim);
gl->Viewport (0, 0, out_width, out_height);
gl->ClearColor (0.0, 0.0, 0.0, 0.0);
gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
gst_gl_shader_use (renderer->shader);
gst_gl_shader_set_uniform_matrix_4fv (renderer->shader, "u_transformation", 1,
FALSE, renderer->transformation_matrix);
gl->VertexAttribPointer (renderer->shader_attr_position_loc, 3,
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vertices);
gl->VertexAttribPointer (renderer->shader_attr_texture_loc, 2,
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vertices[3]);
gl->EnableVertexAttribArray (renderer->shader_attr_position_loc);
gl->EnableVertexAttribArray (renderer->shader_attr_texture_loc);
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (GL_TEXTURE_EXTERNAL_OES, renderer->oes_tex_id);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
gl->TexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
gl->DisableVertexAttribArray (renderer->shader_attr_position_loc);
gl->DisableVertexAttribArray (renderer->shader_attr_texture_loc);
if (gl->DrawBuffer)
gl->DrawBuffer (GL_NONE);
/* we are done with the shader */
gst_gl_context_clear_shader (renderer->context);
gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2],
viewport_dim[3]);
gst_gl_context_check_framebuffer_status (renderer->context);
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
return TRUE;
}
static void
_2d_texture_renderer_render (GstGLContext * context,
GstAmc2DTextureRenderer * renderer)
{
gfloat identify_matrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
gfloat yflip_matrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f
};
gfloat transformation_matrix[16] = { 0 };
if (!renderer->initialized) {
if (!_2d_texture_renderer_init (renderer)) {
renderer->result = FALSE;
return;
}
if (!gst_amc_surface_texture_attach_to_gl_context
(renderer->surface_texture, renderer->oes_tex_id,
&renderer->gl_context_error)) {
renderer->result = FALSE;
return;
}
}
if (!gst_amc_surface_texture_update_tex_image (renderer->surface_texture,
&renderer->gl_context_error)) {
renderer->result = FALSE;
return;
}
if (gst_amc_surface_texture_get_transform_matrix (renderer->surface_texture,
transformation_matrix, &renderer->gl_context_error)) {
int i, j;
for (i = 0; i < 16; i += 4) {
renderer->transformation_matrix[i + j] = 0.0f;
for (j = 0; j < 4; ++j) {
renderer->transformation_matrix[i + j] =
(transformation_matrix[i + 0] * yflip_matrix[j + 0])
+ (transformation_matrix[i + 1] * yflip_matrix[j + 4])
+ (transformation_matrix[i + 2] * yflip_matrix[j + 8])
+ (transformation_matrix[i + 3] * yflip_matrix[j + 12]);
}
}
} else {
memcpy (renderer->transformation_matrix, identify_matrix,
sizeof (identify_matrix[0] * 16));
}
if (!_2d_texture_renderer_draw (renderer)) {
renderer->result = FALSE;
return;
}
renderer->result = TRUE;
}
gboolean
gst_amc_2d_texture_renderer_render (GstAmc2DTextureRenderer *
renderer, guint tex_id, GError ** error)
{
g_return_val_if_fail (renderer != NULL, FALSE);
g_return_val_if_fail (tex_id, FALSE);
renderer->tex_id = tex_id;
renderer->result = FALSE;
renderer->gl_context_error = NULL;
gst_gl_context_thread_add (renderer->context,
(GstGLContextThreadFunc) _2d_texture_renderer_render, renderer);
*error = renderer->gl_context_error;
renderer->gl_context_error = NULL;
return renderer->result;
}