/* * GStreamer * Copyright (C) 2013 Matthew Waters * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gl.h" #include "gstglframebuffer.h" GST_DEBUG_CATEGORY_STATIC (gst_gl_framebuffer_debug); #define GST_CAT_DEFAULT gst_gl_framebuffer_debug #define DEBUG_INIT \ GST_DEBUG_CATEGORY_INIT (gst_gl_framebuffer_debug, "glframebuffer", 0, "GL Framebuffer"); G_DEFINE_TYPE_WITH_CODE (GstGLFramebuffer, gst_gl_framebuffer, G_TYPE_OBJECT, DEBUG_INIT); #define GST_GL_FRAMEBUFFER_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_GL_FRAMEBUFFER, GstGLFramebufferPrivate)) static void gst_gl_framebuffer_finalize (GObject * object); struct _GstGLFramebufferPrivate { gint width; gint height; guint fbo; guint depth; }; static void gst_gl_framebuffer_class_init (GstGLFramebufferClass * klass) { g_type_class_add_private (klass, sizeof (GstGLFramebufferPrivate)); G_OBJECT_CLASS (klass)->finalize = gst_gl_framebuffer_finalize; } static void gst_gl_framebuffer_init (GstGLFramebuffer * fbo) { fbo->priv = GST_GL_FRAMEBUFFER_GET_PRIVATE (fbo); } static void gst_gl_framebuffer_finalize (GObject * object) { GstGLFramebuffer *fbo = GST_GL_FRAMEBUFFER (object); if (fbo->context) { gst_object_unref (fbo->context); fbo->context = NULL; } G_OBJECT_CLASS (gst_gl_framebuffer_parent_class)->finalize (object); } GstGLFramebuffer * gst_gl_framebuffer_new (GstGLContext * context) { GstGLFramebuffer *fbo = g_object_new (GST_TYPE_GL_FRAMEBUFFER, NULL); fbo->context = gst_object_ref (context); return fbo; } gboolean gst_gl_framebuffer_generate (GstGLFramebuffer * frame, gint width, gint height, guint * fbo, guint * depth) { GLuint fake_texture = 0; const GstGLFuncs *gl; g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (frame), FALSE); g_return_val_if_fail (fbo != NULL && depth != NULL, FALSE); g_return_val_if_fail (width > 0 && height > 0, FALSE); gl = frame->context->gl_vtable; GST_TRACE ("creating FBO dimensions:%ux%u", width, height); if (!gl->GenFramebuffers) { gst_gl_context_set_error (frame->context, "Context, EXT_framebuffer_object not supported"); return FALSE; } /* setup FBO */ gl->GenFramebuffers (1, fbo); gl->BindFramebuffer (GL_FRAMEBUFFER, *fbo); /* setup the render buffer for depth */ gl->GenRenderbuffers (1, depth); gl->BindRenderbuffer (GL_RENDERBUFFER, *depth); if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) { gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); } if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_GLES2) { gl->RenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); } /* setup a texture to render to */ gl->GenTextures (1, &fake_texture); gl->BindTexture (GL_TEXTURE_2D, fake_texture); gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, 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, *depth); if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) { gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *depth); } if (gl->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { gst_gl_context_set_error (frame->context, "GL framebuffer status incomplete"); gl->DeleteTextures (1, &fake_texture); return FALSE; } /* unbind the FBO */ gl->BindFramebuffer (GL_FRAMEBUFFER, 0); gl->DeleteTextures (1, &fake_texture); return TRUE; } gboolean gst_gl_framebuffer_use (GstGLFramebuffer * frame, gint texture_fbo_width, gint texture_fbo_height, GLuint fbo, GLuint depth_buffer, GLuint texture_fbo, GLCB cb, gint input_tex_width, gint input_tex_height, GLuint input_tex, gdouble proj_param1, gdouble proj_param2, gdouble proj_param3, gdouble proj_param4, GstGLDisplayProjection projection, gpointer stuff) { const GstGLFuncs *gl; #if GST_GL_HAVE_GLES2 GLint viewport_dim[4]; #endif g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (frame), FALSE); g_return_val_if_fail (input_tex_width > 0 && input_tex_height > 0, FALSE); g_return_val_if_fail (texture_fbo_width > 0 && texture_fbo_height > 0, FALSE); g_return_val_if_fail (input_tex != 0, FALSE); g_return_val_if_fail (fbo != 0, FALSE); g_return_val_if_fail (texture_fbo != 0, FALSE); g_return_val_if_fail (cb != NULL, FALSE); gl = frame->context->gl_vtable; GST_TRACE ("Binding v1 FBO %u dimensions:%ux%u with texture:%u " "dimensions:%ux%u", fbo, texture_fbo_width, texture_fbo_height, texture_fbo, input_tex_width, input_tex_height); gl->BindFramebuffer (GL_FRAMEBUFFER, fbo); /*setup a texture to render to */ gl->BindTexture (GL_TEXTURE_2D, texture_fbo); /* attach the texture to the FBO to renderer to */ gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_fbo, 0); gst_gl_context_clear_shader (frame->context); #if GST_GL_HAVE_OPENGL if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) { gl->PushAttrib (GL_VIEWPORT_BIT); gl->MatrixMode (GL_PROJECTION); gl->PushMatrix (); gl->LoadIdentity (); switch (projection) { case GST_GL_DISPLAY_PROJECTION_ORTHO2D: gluOrtho2D (proj_param1, proj_param2, proj_param3, proj_param4); break; case GST_GL_DISPLAY_PROJECTION_PERSPECTIVE: gluPerspective (proj_param1, proj_param2, proj_param3, proj_param4); break; default: gst_gl_context_set_error (frame->context, "Unknow fbo projection %d", projection); } gl->MatrixMode (GL_MODELVIEW); gl->PushMatrix (); gl->LoadIdentity (); } #endif #if GST_GL_HAVE_GLES2 if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_GLES2) gl->GetIntegerv (GL_VIEWPORT, viewport_dim); #endif gl->Viewport (0, 0, texture_fbo_width, texture_fbo_height); #if GST_GL_HAVE_OPENGL if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) { const GLenum rt[] = { GL_COLOR_ATTACHMENT0 }; gl->DrawBuffers (1, rt); } #endif gl->ClearColor (0.0, 0.0, 0.0, 0.0); gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); cb (input_tex_width, input_tex_height, input_tex, stuff); #if GST_GL_HAVE_OPENGL if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) { const GLenum rt[] = { GL_NONE }; gl->DrawBuffers (1, rt); gl->MatrixMode (GL_PROJECTION); gl->PopMatrix (); gl->MatrixMode (GL_MODELVIEW); gl->PopMatrix (); gl->PopAttrib (); } #endif #if GST_GL_HAVE_GLES2 if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_GLES2) { gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2], viewport_dim[3]); } #endif gl->BindFramebuffer (GL_FRAMEBUFFER, 0); return TRUE; } gboolean gst_gl_framebuffer_use_v2 (GstGLFramebuffer * frame, gint texture_fbo_width, gint texture_fbo_height, GLuint fbo, GLuint depth_buffer, GLuint texture_fbo, GLCB_V2 cb, gpointer stuff) { const GstGLFuncs *gl; GLint viewport_dim[4]; g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (frame), FALSE); g_return_val_if_fail (texture_fbo_width > 0 && texture_fbo_height > 0, FALSE); g_return_val_if_fail (fbo != 0, FALSE); g_return_val_if_fail (texture_fbo != 0, FALSE); g_return_val_if_fail (cb != NULL, FALSE); gl = frame->context->gl_vtable; GST_TRACE ("Binding v2 FBO %u dimensions:%ux%u with texture:%u ", fbo, texture_fbo_width, texture_fbo_height, texture_fbo); gl->BindFramebuffer (GL_FRAMEBUFFER, fbo); /* setup a texture to render to */ gl->BindTexture (GL_TEXTURE_2D, texture_fbo); /* attach the texture to the FBO to renderer to */ gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_fbo, 0); gl->GetIntegerv (GL_VIEWPORT, viewport_dim); gl->Viewport (0, 0, texture_fbo_width, texture_fbo_height); if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) gl->DrawBuffer (GL_COLOR_ATTACHMENT0); gl->ClearColor (0.0, 0.0, 0.0, 0.0); gl->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* the opengl scene */ cb (stuff); if (gst_gl_context_get_gl_api (frame->context) & GST_GL_API_OPENGL) gl->DrawBuffer (GL_NONE); gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2], viewport_dim[3]); gl->BindFramebuffer (GL_FRAMEBUFFER, 0); return TRUE; } void gst_gl_framebuffer_delete (GstGLFramebuffer * frame, guint fbo, guint depth) { const GstGLFuncs *gl; g_return_if_fail (GST_IS_GL_FRAMEBUFFER (frame)); gl = frame->context->gl_vtable; GST_TRACE ("Deleting FBO %u", fbo); if (fbo) { gl->DeleteFramebuffers (1, &fbo); } if (depth) { gl->DeleteRenderbuffers (1, &depth); } }