diff --git a/gst-libs/gst/gl/gstglbuffer.c b/gst-libs/gst/gl/gstglbuffer.c new file mode 100644 index 0000000000..aaa39677e6 --- /dev/null +++ b/gst-libs/gst/gl/gstglbuffer.c @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglbuffer.h" + +static GObjectClass* gst_gl_buffer_parent_class; + +static void +gst_gl_buffer_finalize (GstGLBuffer* buffer) +{ + //wait clear textures end, blocking call + gst_gl_display_clearTexture (buffer->display, buffer->texture, + buffer->texture_u, buffer->texture_v); + + g_object_unref (buffer->display); + + GST_MINI_OBJECT_CLASS (gst_gl_buffer_parent_class)-> + finalize (GST_MINI_OBJECT (buffer)); +} + +static void +gst_gl_buffer_init (GstGLBuffer* buffer, gpointer g_class) +{ + buffer->display = NULL; + buffer->video_format = 0; + buffer->texture = 0; + buffer->texture_u = 0; + buffer->texture_v = 0; + buffer->width = 0; + buffer->height = 0; +} + +static void +gst_gl_buffer_class_init (gpointer g_class, gpointer class_data) +{ + GstMiniObjectClass* mini_object_class = GST_MINI_OBJECT_CLASS (g_class); + + gst_gl_buffer_parent_class = g_type_class_peek_parent (g_class); + + mini_object_class->finalize = (GstMiniObjectFinalizeFunction) + gst_gl_buffer_finalize; +} + + +GType +gst_gl_buffer_get_type (void) +{ + static GType _gst_gl_buffer_type; + + if (G_UNLIKELY (_gst_gl_buffer_type == 0)) { + static const GTypeInfo info = { + sizeof (GstBufferClass), + NULL, + NULL, + gst_gl_buffer_class_init, + NULL, + NULL, + sizeof (GstGLBuffer), + 0, + (GInstanceInitFunc) gst_gl_buffer_init, + NULL + }; + _gst_gl_buffer_type = g_type_register_static (GST_TYPE_BUFFER, + "GstGLBuffer", &info, 0); + } + return _gst_gl_buffer_type; +} + + +GstGLBuffer* +gst_gl_buffer_new_from_video_format (GstGLDisplay* display, + GstVideoFormat video_format, gint context_width, gint context_height, + gint width, gint height) +{ + GstGLBuffer *buffer; + + g_return_val_if_fail (video_format != GST_VIDEO_FORMAT_UNKNOWN, NULL); + g_return_val_if_fail (display != NULL, NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + buffer = (GstGLBuffer *) gst_mini_object_new (GST_TYPE_GL_BUFFER); + + buffer->display = g_object_ref (display); + buffer->width = width; + buffer->height = height; + buffer->video_format = video_format; + GST_BUFFER_SIZE (buffer) = gst_gl_buffer_format_get_size (video_format, context_width, context_height); + + //blocking call, init texture + gst_gl_display_textureRequested (buffer->display, buffer->video_format, + buffer->width, buffer->height, + &buffer->texture, &buffer->texture_u, &buffer->texture_v) ; + + return buffer; +} + + +int +gst_gl_buffer_format_get_size (GstVideoFormat format, int width, int height) +{ + /* this is not strictly true, but it's used for compatibility with + * queue and BaseTransform */ + return width * height * 4; +} + +gboolean +gst_gl_buffer_format_parse_caps (GstCaps * caps, GstVideoFormat * format, + gint* width, gint* height) +{ + GstStructure *structure; + gboolean ret; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_has_name (structure, "video/x-raw-gl")) + return FALSE; + + ret = gst_structure_get_int (structure, "width", width); + ret &= gst_structure_get_int (structure, "height", height); + + return ret; +} diff --git a/gst-libs/gst/gl/gstglbuffer.h b/gst-libs/gst/gl/gstglbuffer.h new file mode 100644 index 0000000000..cfb3a4fd99 --- /dev/null +++ b/gst-libs/gst/gl/gstglbuffer.h @@ -0,0 +1,54 @@ +#ifndef _GST_GL_BUFFER_H_ +#define _GST_GL_BUFFER_H_ + +#include +#include + +#include "gstgldisplay.h" + +typedef struct _GstGLBuffer GstGLBuffer; + +#define GST_TYPE_GL_BUFFER (gst_gl_buffer_get_type()) + +#define GST_IS_GL_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GL_BUFFER)) +#define GST_GL_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GL_BUFFER, GstGLBuffer)) + + + +struct _GstGLBuffer { + GstBuffer buffer; + + GstGLDisplay *display; + + GstVideoFormat video_format; + + GLuint texture; + GLuint texture_u; + GLuint texture_v; + + gint width; + gint height; +}; + +GType gst_gl_buffer_get_type (void); + +#define gst_gl_buffer_ref(x) ((GstGLBuffer *)(gst_buffer_ref((GstBuffer *)(x)))) +#define gst_gl_buffer_unref(x) (gst_buffer_unref((GstBuffer *)(x))) + +GstGLBuffer* gst_gl_buffer_new_from_video_format (GstGLDisplay* display, GstVideoFormat format, + gint context_width, gint context_height, + gint width, gint height); +gint gst_gl_buffer_format_get_size (GstVideoFormat format, gint width, gint height); +gboolean gst_gl_buffer_format_parse_caps (GstCaps* caps, GstVideoFormat* format, + gint* width, gint* height); + + +#define GST_GL_VIDEO_CAPS \ + "video/x-raw-gl," \ + "width=(int)[1,1920]," \ + "height=(int)[1,1080]," \ + "pixel-aspect-ratio=(fraction)1/1," \ + "framerate=(fraction)[0/1,100/1]" + +#endif + diff --git a/gst-libs/gst/gl/gstgldisplay.c b/gst-libs/gst/gl/gstgldisplay.c new file mode 100644 index 0000000000..d73d7cf991 --- /dev/null +++ b/gst-libs/gst/gl/gstgldisplay.c @@ -0,0 +1,1909 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstgldisplay.h" +#include + + +//------------------------------------------------------------------------------------------- +//--------------------------- TODO ---------------------------------------------------------- +// - send EOS when the user click on the window cross +//------------------------------------------------------------------------------------------- + + +//------------------------------------------------------------ +//-------------------- Private déclarations ------------------ +//------------------------------------------------------------ +static void gst_gl_display_finalize (GObject * object); +static gpointer gst_gl_display_glutThreadFunc (GstGLDisplay* display); +static void gst_gl_display_glutCreateWindow (GstGLDisplay* display); +static void gst_gl_display_glutDestroyWindow (GstGLDisplay* display); +static void gst_gl_display_glutSetVisibleWindow (GstGLDisplay* display); +static void gst_gl_display_glutPrepareTexture (GstGLDisplay* display); +static void gst_gl_display_glutUpdateTexture (GstGLDisplay* display); +static void gst_gl_display_glutCleanTexture (GstGLDisplay* display); +static void gst_gl_display_glutUpdateVideo (GstGLDisplay* display); +static void gst_gl_display_glutPostRedisplay (GstGLDisplay* display); +static void gst_gl_display_glut_idle (void); +static void gst_gl_display_glutDispatchAction (GstGLDisplayMsg *msg); +static gboolean gst_gl_display_checkMsgValidity (GstGLDisplayMsg *msg); +void gst_gl_display_lock (GstGLDisplay* display); +void gst_gl_display_unlock (GstGLDisplay* display); +void gst_gl_display_postMessage (GstGLDisplayAction action, GstGLDisplay* display); +void gst_gl_display_onReshape(gint width, gint height); +void gst_gl_display_draw (void); +void gst_gl_display_onClose (void); +void gst_gl_display_make_texture (GstGLDisplay* display); +void gst_gl_display_fill_texture (GstGLDisplay* display); +void gst_gl_display_draw_texture (GstGLDisplay* display); +void gst_gl_display_draw_graphic (GstGLDisplay* display); +void gst_gl_display_fill_video (GstGLDisplay* display); +GLhandleARB gst_gl_display_loadGLSLprogram (gchar* textFProgram); +void checkFramebufferStatus(void); +GST_BOILERPLATE (GstGLDisplay, gst_gl_display, GObject, G_TYPE_OBJECT); + + +//------------------------------------------------------------ +//-------------------- Glut context management --------------- +//------------------------------------------------------------ + +//(key=int glutWinId) and (value=GstGLDisplay *display) +static GHashTable *gst_gl_display_map = NULL; + +//all glut functions and opengl primitives are called in this thread +static GThread *gst_gl_display_glutThread = NULL; + +//-timepoped by glutIdleFunc +static GAsyncQueue *gst_gl_display_messageQueue = NULL; + + +//------------------------------------------------------------ +//---------------------- For klass GstGLDisplay --------------- +//------------------------------------------------------------ + +static void +gst_gl_display_base_init (gpointer g_class) +{ +} + +static void +gst_gl_display_class_init (GstGLDisplayClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = gst_gl_display_finalize; +} + + +static void +gst_gl_display_init (GstGLDisplay *display, GstGLDisplayClass *klass) +{ + display->mutex = g_mutex_new (); + display->texturePool = g_queue_new (); + display->cond_make = g_cond_new (); + display->cond_fill = g_cond_new (); + display->cond_clear = g_cond_new (); + display->cond_video = g_cond_new (); + display->cond_create = g_cond_new (); + display->cond_destroy = g_cond_new (); + + display->fbo = 0; + display->depthBuffer = 0; + display->textureFBO = 0; + display->textureFBOWidth = 0; + display->textureFBOHeight = 0; + + display->graphicFBO = 0; + display->graphicDepthBuffer = 0; + display->graphicTexture = 0; + + display->requestedTexture = 0; + display->requestedTexture_u = 0; + display->requestedTexture_v = 0; + display->requestedVideo_format = 0; + display->requestedTextureWidth = 0; + display->requestedTextureHeight = 0; + + display->candidateTexture = 0; + display->candidateTexture_u = 0; + display->candidateTexture_v = 0; + display->candidateVideo_format = 0; + display->candidateTextureWidth = 0; + display->candidateTextureHeight = 0; + display->candidateData = NULL; + + display->currentTexture = 0; + display->currentTexture_u = 0; + display->currentTexture_v = 0; + display->currentVideo_format = 0; + display->currentTextureWidth = 0; + display->currentTextureHeight = 0; + + display->textureTrash = 0; + display->textureTrash_u = 0; + display->textureTrash_v = 0; + + display->videoFBO = 0; + display->videoDepthBuffer = 0; + display->videoTexture = 0; + display->videoTexture_u = 0; + display->videoTexture_v = 0; + display->outputWidth = 0; + display->outputHeight = 0; + display->outputVideo_format = 0; + display->outputData = NULL; + + display->glutWinId = -1; + display->winId = 0; + display->win_xpos = 0; + display->win_ypos = 0; + display->glcontext_width = 0; + display->glcontext_height = 0; + display->visible = FALSE; + display->clientReshapeCallback = NULL; + display->clientDrawCallback = NULL; + display->title = g_string_new ("OpenGL renderer "); + + display->GLSLProgram_YUY2 = 0; + display->GLSLProgram_UYVY = 0; + display->GLSLProgram_I420_YV12 = 0; + display->GLSLProgram_AYUV = 0; + + display->GLSLProgram_to_YUY2 = 0; + display->GLSLProgram_to_UYVY = 0; + display->GLSLProgram_to_I420_YV12 = 0; + display->GLSLProgram_to_AYUV = 0; + + //YUY2:r,g,a + //UYVY:a,b,r + display->textFProgram_YUY2_UYVY = + "uniform sampler2DRect Ytex, UVtex;\n" + "void main(void) {\n" + " float fx, fy, y, u, v, r, g, b;\n" + " fx = gl_TexCoord[0].x;\n" + " fy = gl_TexCoord[0].y;\n" + " y = texture2DRect(Ytex,vec2(fx,fy)).%c;\n" + " u = texture2DRect(UVtex,vec2(fx*0.5,fy)).%c;\n" + " v = texture2DRect(UVtex,vec2(fx*0.5,fy)).%c;\n" + " y=1.164*(y-0.0627);\n" + " u=u-0.5;\n" + " v=v-0.5;\n" + " r = y+1.5958*v;\n" + " g = y-0.39173*u-0.81290*v;\n" + " b = y+2.017*u;\n" + " gl_FragColor = vec4(r, g, b, 1.0);\n" + "}\n"; + + display->textFProgram_I420_YV12 = + "uniform sampler2DRect Ytex,Utex,Vtex;\n" + "void main(void) {\n" + " float r,g,b,y,u,v;\n" + " vec2 nxy=gl_TexCoord[0].xy;\n" + " y=texture2DRect(Ytex,nxy*0.5).r;\n" + " u=texture2DRect(Utex,nxy).r;\n" + " v=texture2DRect(Vtex,nxy*0.5).r;\n" + " y=1.1643*(y-0.0625);\n" + " u=u-0.5;\n" + " v=v-0.5;\n" + " r=y+1.5958*v;\n" + " g=y-0.39173*u-0.81290*v;\n" + " b=y+2.017*u;\n" + " gl_FragColor=vec4(r,g,b,1.0);\n" + "}\n"; + + display->textFProgram_AYUV = + "uniform sampler2DRect tex;\n" + "void main(void) {\n" + " float r,g,b,y,u,v;\n" + " vec2 nxy=gl_TexCoord[0].xy;\n" + " y=texture2DRect(tex,nxy).r;\n" + " u=texture2DRect(tex,nxy).g;\n" + " v=texture2DRect(tex,nxy).b;\n" + " y=1.1643*(y-0.0625);\n" + " u=u-0.5;\n" + " v=v-0.5;\n" + " r=y+1.5958*v;\n" + " g=y-0.39173*u-0.81290*v;\n" + " b=y+2.017*u;\n" + " gl_FragColor=vec4(r,g,b,1.0);\n" + "}\n"; + + //YUY2:y2,u,y1,v + //UYVY:v,y1,u,y2 + display->textFProgram_to_YUY2_UYVY = + "uniform sampler2DRect tex;\n" + "void main(void) {\n" + " float fx,fy,r,g,b,r2,g2,b2,y1,y2,u,v;\n" + " fx = gl_TexCoord[0].x;\n" + " fy = gl_TexCoord[0].y;\n" + " r=texture2DRect(tex,vec2(fx*2.0,fy)).r;\n" + " g=texture2DRect(tex,vec2(fx*2.0,fy)).g;\n" + " b=texture2DRect(tex,vec2(fx*2.0,fy)).b;\n" + " r2=texture2DRect(tex,vec2(fx*2.0+1.0,fy)).r;\n" + " g2=texture2DRect(tex,vec2(fx*2.0+1.0,fy)).g;\n" + " b2=texture2DRect(tex,vec2(fx*2.0+1.0,fy)).b;\n" + " y1=0.299011*r + 0.586987*g + 0.114001*b;\n" + " y2=0.299011*r2 + 0.586987*g2 + 0.114001*b2;\n" + " u=-0.148246*r -0.29102*g + 0.439266*b;\n" + " v=0.439271*r - 0.367833*g - 0.071438*b ;\n" + " y1=0.858885*y1 + 0.0625;\n" + " y2=0.858885*y2 + 0.0625;\n" + " u=u + 0.5;\n" + " v=v + 0.5;\n" + " gl_FragColor=vec4(%s);\n" + "}\n"; + + display->textFProgram_to_I420_YV12 = + "uniform sampler2DRect tex;\n" + "uniform float w, h;\n" + "void main(void) {\n" + " float r,g,b,r2,b2,g2,y,u,v;\n" + " vec2 nxy=gl_TexCoord[0].xy;\n" + " vec2 nxy2=mod(2.0*nxy, vec2(w, h));\n" + " r=texture2DRect(tex,nxy).r;\n" + " g=texture2DRect(tex,nxy).g;\n" + " b=texture2DRect(tex,nxy).b;\n" + " r2=texture2DRect(tex,nxy2).r;\n" + " g2=texture2DRect(tex,nxy2).g;\n" + " b2=texture2DRect(tex,nxy2).b;\n" + " y=0.299011*r + 0.586987*g + 0.114001*b;\n" + " u=-0.148246*r2 -0.29102*g2 + 0.439266*b2;\n" + " v=0.439271*r2 - 0.367833*g2 - 0.071438*b2 ;\n" + " y=0.858885*y + 0.0625;\n" + " u=u + 0.5;\n" + " v=v + 0.5;\n" + " gl_FragData[0] = vec4(y, 0.0, 0.0, 1.0);\n" + " gl_FragData[1] = vec4(u, 0.0, 0.0, 1.0);\n" + " gl_FragData[2] = vec4(v, 0.0, 0.0, 1.0);\n" + "}\n"; + + display->textFProgram_to_AYUV = + "uniform sampler2DRect tex;\n" + "void main(void) {\n" + " float r,g,b,y,u,v;\n" + " vec2 nxy=gl_TexCoord[0].xy;\n" + " r=texture2DRect(tex,nxy).r;\n" + " g=texture2DRect(tex,nxy).g;\n" + " b=texture2DRect(tex,nxy).b;\n" + " y=0.299011*r + 0.586987*g + 0.114001*b;\n" + " u=-0.148246*r -0.29102*g + 0.439266*b;\n" + " v=0.439271*r - 0.367833*g - 0.071438*b ;\n" + " y=0.858885*y + 0.0625;\n" + " u=u + 0.5;\n" + " v=v + 0.5;\n" + " gl_FragColor=vec4(y,u,v,1.0);\n" + "}\n"; +} + +static void +gst_gl_display_finalize (GObject *object) +{ + GstGLDisplay *display = GST_GL_DISPLAY (object); + + //request glut window destruction + //blocking call because display must be alive + gst_gl_display_lock (display); + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_DESTROY, display); + g_cond_wait (display->cond_destroy, display->mutex); + gst_gl_display_unlock (display); + + if (display->texturePool) { + g_queue_free (display->texturePool); + display->texturePool = NULL; + } + + if (display->title) { + g_string_free (display->title, TRUE); + display->title = NULL; + } + if (display->mutex) { + g_mutex_free (display->mutex); + display->mutex = NULL; + } + if (display->cond_make) { + g_cond_free (display->cond_make); + display->cond_make = NULL; + } + if (display->cond_fill) { + g_cond_free (display->cond_fill); + display->cond_fill = NULL; + } + if (display->cond_clear) { + g_cond_free (display->cond_clear); + display->cond_clear = NULL; + } + if (display->cond_video) { + g_cond_free (display->cond_video); + display->cond_video = NULL; + } + if (display->cond_create) { + g_cond_free (display->cond_create); + display->cond_create = NULL; + } + if (display->cond_destroy) { + g_cond_free (display->cond_destroy); + display->cond_destroy = NULL; + } + if (display->clientReshapeCallback) + display->clientReshapeCallback = NULL; + if (display->clientDrawCallback) + display->clientDrawCallback = NULL; + + //at this step, the next condition imply that the last display has been pushed + if (g_hash_table_size (gst_gl_display_map) == 0) + { + g_thread_join (gst_gl_display_glutThread); + g_print ("Glut thread joined\n"); + gst_gl_display_glutThread = NULL; + g_async_queue_unref (gst_gl_display_messageQueue); + g_hash_table_unref (gst_gl_display_map); + } +} + + +/* The glut thread handles glut events and GstGLDisplayMsg messages */ +static gpointer +gst_gl_display_glutThreadFunc (GstGLDisplay *display) +{ + static char *argv = "gst-launch-0.10.exe"; + static gint argc = 1; + + glutInit(&argc, &argv); + glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); + + glutIdleFunc (gst_gl_display_glut_idle); + + gst_gl_display_lock (display); + gst_gl_display_glutCreateWindow (display); + gst_gl_display_unlock (display); + + g_print ("Glut mainLoop start\n"); + glutMainLoop (); + g_print ("Glut mainLoop exited\n"); + + return NULL; +} + + +/* Called by the idle function or when creating glut_thread */ +static void +gst_gl_display_glutCreateWindow (GstGLDisplay *display) +{ + gint glutWinId = 0; + GList *keys = NULL; + gchar buffer[5]; + GLenum err = 0; + + //prepare opengl context + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); + glutInitWindowPosition(display->win_xpos, display->win_ypos); + glutInitWindowSize(display->glcontext_width, display->glcontext_height); + + //create opengl context + sprintf_s(buffer, 5, "%d", glutWinId); + display->title = g_string_append (display->title, buffer); + glutWinId = glutCreateWindow (display->title->str, display->winId); + + if (display->visible) + glutShowWindow (); + else + glutHideWindow (); + + //Init glew + err = glewInit(); + if (err != GLEW_OK) + g_print ("Error: %s\n", glewGetErrorString(err)); + else + g_print ("Context %d, Using GLEW %s\n", glutWinId, glewGetString(GLEW_VERSION)); + + if (GLEW_EXT_framebuffer_object) + { + g_print ("Context %d, EXT_framebuffer_object supported: yes\n", glutWinId); + + //-- init intput frame buffer object (video -> GL) + + //setup FBO + glGenFramebuffersEXT (1, &display->fbo); + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->fbo); + + //setup the render buffer for depth + glGenRenderbuffersEXT(1, &display->depthBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, display->depthBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, + display->textureFBOWidth, display->textureFBOHeight); + + //setup a texture to render to + glGenTextures (1, &display->textureFBO); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, display->textureFBO); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + display->textureFBOWidth, display->textureFBOHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //attach the texture to the FBO to renderer to + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, display->textureFBO, 0); + + //attach the depth render buffer to the FBO + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, display->depthBuffer); + + g_assert (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) == + GL_FRAMEBUFFER_COMPLETE_EXT); + + //unbind the FBO + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + //-- init graphic frame buffer object (GL texture -> GL scene) + + //setup FBO + glGenFramebuffersEXT (1, &display->graphicFBO); + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->graphicFBO); + + //setup the render buffer for depth + glGenRenderbuffersEXT(1, &display->graphicDepthBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, display->graphicDepthBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, + display->glcontext_width, display->glcontext_height); + + //setup a texture to render to + glGenTextures (1, &display->graphicTexture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, display->graphicTexture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + display->glcontext_width, display->glcontext_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //attach the texture to the FBO to renderer to + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, display->graphicTexture, 0); + + //attach the depth render buffer to the FBO + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, display->graphicDepthBuffer); + + g_assert (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) == + GL_FRAMEBUFFER_COMPLETE_EXT); + + //unbind the FBO + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + //-- init output frame buffer object (GL -> video) + + display->outputWidth = display->glcontext_width; + display->outputHeight = display->glcontext_height; + + //setup FBO + glGenFramebuffersEXT (1, &display->videoFBO); + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->videoFBO); + + //setup the render buffer for depth + glGenRenderbuffersEXT(1, &display->videoDepthBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, display->videoDepthBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, + display->outputWidth, display->outputHeight); + + //setup a first texture to render to + glGenTextures (1, &display->videoTexture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, display->videoTexture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + display->outputWidth, display->outputHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //attach the first texture to the FBO to renderer to + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, display->videoTexture, 0); + + //setup a second texture to render to + glGenTextures (1, &display->videoTexture_u); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, display->videoTexture_u); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + display->outputWidth, display->outputHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //attach the second texture to the FBO to renderer to + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, + GL_TEXTURE_RECTANGLE_ARB, display->videoTexture_u, 0); + + //setup a third texture to render to + glGenTextures (1, &display->videoTexture_v); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, display->videoTexture_v); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + display->outputWidth, display->outputHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //attach the third texture to the FBO to renderer to + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, + GL_TEXTURE_RECTANGLE_ARB, display->videoTexture_v, 0); + + //attach the depth render buffer to the FBO + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, display->videoDepthBuffer); + + checkFramebufferStatus(); + + g_assert (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) == + GL_FRAMEBUFFER_COMPLETE_EXT); + + //unbind the FBO + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + display->multipleRT[0] = GL_COLOR_ATTACHMENT0_EXT; + display->multipleRT[1] = GL_COLOR_ATTACHMENT1_EXT; + display->multipleRT[2] = GL_COLOR_ATTACHMENT2_EXT; + + } + else + { + g_print ("Context %d, EXT_framebuffer_object supported: no\n", glutWinId); + g_assert_not_reached (); + } + + //check if fragment program is available, then load them + if (GLEW_ARB_vertex_program) + { + gchar program[2048]; + + g_print ("Context %d, ARB_fragment_program supported: yes\n", glutWinId); + + //from video to texture + + sprintf_s (program, 2048, display->textFProgram_YUY2_UYVY, 'r', 'g', 'a'); + display->GLSLProgram_YUY2 = gst_gl_display_loadGLSLprogram (program); + + sprintf_s (program, 2048, display->textFProgram_YUY2_UYVY, 'a', 'b', 'r'); + display->GLSLProgram_UYVY = gst_gl_display_loadGLSLprogram (program); + + display->GLSLProgram_I420_YV12 = gst_gl_display_loadGLSLprogram (display->textFProgram_I420_YV12); + + display->GLSLProgram_AYUV = gst_gl_display_loadGLSLprogram (display->textFProgram_AYUV); + + //from texture to video + + sprintf_s (program, 2048, display->textFProgram_to_YUY2_UYVY, "y2,u,y1,v"); + display->GLSLProgram_to_YUY2 = gst_gl_display_loadGLSLprogram (program); + + sprintf_s (program, 2048, display->textFProgram_to_YUY2_UYVY, "v,y1,u,y2"); + display->GLSLProgram_to_UYVY = gst_gl_display_loadGLSLprogram (program); + + display->GLSLProgram_to_I420_YV12 = gst_gl_display_loadGLSLprogram (display->textFProgram_to_I420_YV12); + + display->GLSLProgram_to_AYUV = gst_gl_display_loadGLSLprogram (display->textFProgram_to_AYUV); + } + else + { + g_print ("Context %d, ARB_fragment_program supported: no\n", glutWinId); + g_assert_not_reached (); + } + + //setup callbacks + glutReshapeFunc (gst_gl_display_onReshape); + glutDisplayFunc (gst_gl_display_draw); + glutCloseFunc (gst_gl_display_onClose); + + //insert glut context to the map + display->glutWinId = glutWinId; + g_hash_table_insert (gst_gl_display_map, glutWinId, display); + + //check glut id validity + g_assert (glutGetWindow() == glutWinId); + g_print ("Context %d initialized\n", display->glutWinId); + + //release display constructor + g_cond_signal (display->cond_create); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutDestroyWindow (GstGLDisplay *display) +{ + glutSetWindow (display->glutWinId); + glutReshapeFunc (NULL); + glutDestroyWindow (display->glutWinId); + glUseProgramObjectARB (0); + + glDeleteObjectARB (display->GLSLProgram_YUY2); + glDeleteObjectARB (display->GLSLProgram_UYVY); + glDeleteObjectARB (display->GLSLProgram_I420_YV12); + glDeleteObjectARB (display->GLSLProgram_AYUV); + + glDeleteObjectARB (display->GLSLProgram_to_YUY2); + glDeleteObjectARB (display->GLSLProgram_to_UYVY); + glDeleteObjectARB (display->GLSLProgram_to_I420_YV12); + glDeleteObjectARB (display->GLSLProgram_to_AYUV); + + glDeleteFramebuffersEXT (1, &display->fbo); + glDeleteRenderbuffersEXT(1, &display->depthBuffer); + glDeleteTextures (1, &display->textureFBO); + + glDeleteFramebuffersEXT (1, &display->graphicFBO); + glDeleteRenderbuffersEXT(1, &display->graphicDepthBuffer); + glDeleteTextures (1, &display->graphicTexture); + + glDeleteFramebuffersEXT (1, &display->videoFBO); + glDeleteRenderbuffersEXT(1, &display->videoDepthBuffer); + glDeleteTextures (1, &display->videoTexture); + glDeleteTextures (1, &display->videoTexture_u); + glDeleteTextures (1, &display->videoTexture_v); + + //clean up the texture pool + while (g_queue_get_length (display->texturePool)) + { + GstGLDisplayTex* tex = g_queue_pop_head (display->texturePool); + + //delete textures + glDeleteTextures (1, &tex->texture); + if (tex->texture_u) { + glDeleteTextures (1, &tex->texture_u); + } + if (tex->texture_v) { + glDeleteTextures (1, &tex->texture_v); + } + } + + g_hash_table_remove (gst_gl_display_map, display->glutWinId); + g_print ("glut window destroyed: %d\n", display->glutWinId); + + //if the map is empty, leaveMainloop and join the thread + if (g_hash_table_size (gst_gl_display_map) == 0) + glutLeaveMainLoop (); + + //release display destructor + g_cond_signal (display->cond_destroy); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutSetVisibleWindow (GstGLDisplay *display) +{ + glutSetWindow (display->glutWinId); + if (display->visible) + glutShowWindow (); + else + glutHideWindow (); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutPrepareTexture (GstGLDisplay * display) +{ + glutSetWindow (display->glutWinId); + gst_gl_display_make_texture (display); + g_cond_signal (display->cond_make); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutUpdateTexture (GstGLDisplay * display) +{ + glutSetWindow (display->glutWinId); + gst_gl_display_fill_texture (display); + gst_gl_display_draw_texture (display); + g_cond_signal (display->cond_fill); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutCleanTexture (GstGLDisplay * display) +{ + GstGLDisplayTex* tex = NULL; + + glutSetWindow (display->glutWinId); + + //contructuct a texture pool element + tex = g_new0 (GstGLDisplayTex, 1); + tex->texture = display->textureTrash; + tex->texture_u = display->textureTrash_u; + tex->texture_v = display->textureTrash_v; + + display->textureTrash = 0; + display->textureTrash_u = 0; + display->textureTrash_v = 0; + + //add tex to the pool, it makes texture allocation reusable + g_queue_push_tail (display->texturePool, tex); + + g_cond_signal (display->cond_clear); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutUpdateVideo (GstGLDisplay * display) +{ + glutSetWindow (display->glutWinId); + gst_gl_display_draw_graphic (display); + gst_gl_display_fill_video (display); + g_cond_signal (display->cond_video); +} + + +/* Called by the idle function */ +static void +gst_gl_display_glutPostRedisplay (GstGLDisplay * display) +{ + glutSetWindow (display->glutWinId); + glutPostRedisplay (); +} + + +/* Called continuously from freeglut while no events are pending */ +static void +gst_gl_display_glut_idle (void) +{ + GTimeVal timeout; + GstGLDisplayMsg *msg; + + //check for pending actions that require a glut context + g_get_current_time (&timeout); + g_time_val_add (&timeout, 1000000L); //timeout 1 sec + msg = g_async_queue_timed_pop (gst_gl_display_messageQueue, &timeout); + if (msg) + { + if (gst_gl_display_checkMsgValidity (msg)) + gst_gl_display_glutDispatchAction (msg); + while (g_async_queue_length (gst_gl_display_messageQueue)) + { + msg = g_async_queue_pop (gst_gl_display_messageQueue); + if (gst_gl_display_checkMsgValidity (msg)) + gst_gl_display_glutDispatchAction (msg); + } + } + else g_print ("timeout reached in idle func\n"); +} + + +/* Called by the glut idle function */ +static void +gst_gl_display_glutDispatchAction (GstGLDisplayMsg* msg) +{ + gst_gl_display_lock (msg->display); + switch (msg->action) + { + case GST_GL_DISPLAY_ACTION_CREATE: + gst_gl_display_glutCreateWindow (msg->display); + break; + case GST_GL_DISPLAY_ACTION_DESTROY: + gst_gl_display_glutDestroyWindow (msg->display); + break; + case GST_GL_DISPLAY_ACTION_VISIBLE: + gst_gl_display_glutSetVisibleWindow (msg->display); + break; + case GST_GL_DISPLAY_ACTION_PREPARE: + gst_gl_display_glutPrepareTexture (msg->display); + break; + case GST_GL_DISPLAY_ACTION_CHANGE: + gst_gl_display_glutUpdateTexture (msg->display); + break; + case GST_GL_DISPLAY_ACTION_CLEAR: + gst_gl_display_glutCleanTexture (msg->display); + break; + case GST_GL_DISPLAY_ACTION_VIDEO: + gst_gl_display_glutUpdateVideo (msg->display); + break; + case GST_GL_DISPLAY_ACTION_REDISPLAY: + gst_gl_display_glutPostRedisplay (msg->display); + break; + default: + g_assert_not_reached (); + } + gst_gl_display_unlock (msg->display); + g_free (msg); +} + + +/* Return false if the message is out of date */ +static gboolean +gst_gl_display_checkMsgValidity (GstGLDisplayMsg *msg) +{ + gboolean valid = TRUE; + + switch (msg->action) + { + case GST_GL_DISPLAY_ACTION_CREATE: + valid = TRUE; + break; + case GST_GL_DISPLAY_ACTION_DESTROY: + case GST_GL_DISPLAY_ACTION_VISIBLE: + case GST_GL_DISPLAY_ACTION_PREPARE: + case GST_GL_DISPLAY_ACTION_CHANGE: + case GST_GL_DISPLAY_ACTION_CLEAR: + case GST_GL_DISPLAY_ACTION_VIDEO: + case GST_GL_DISPLAY_ACTION_REDISPLAY: + //msg is out of date if the associated display is not in the map + if (!g_hash_table_lookup (gst_gl_display_map, msg->glutWinId)) + valid = FALSE; + break; + default: + g_assert_not_reached (); + } + + return valid; +} + + +//------------------------------------------------------------ +//---------------------- For each GstGLDisplay --------------- +//------------------------------------------------------------ + +GstGLDisplay * +gst_gl_display_new (void) +{ + return g_object_new (GST_TYPE_GL_DISPLAY, NULL); +} + +/* Init an opengl context */ +void +gst_gl_display_initGLContext (GstGLDisplay *display, + GLint x, GLint y, + GLint graphic_width, GLint graphic_height, + GLint video_width, GLint video_height, + gulong winId, + gboolean visible) +{ + gst_gl_display_lock (display); + + display->winId = winId; + display->win_xpos = x; + display->win_ypos = y; + display->glcontext_width = graphic_width; + display->glcontext_height = graphic_height; + display->textureFBOWidth = video_width; + display->textureFBOHeight = video_height; + display->visible = visible; + + //if no glut_thread exists, create it with a window associated to the display + if (!gst_gl_display_map) + { + gst_gl_display_messageQueue = g_async_queue_new (); + gst_gl_display_map = g_hash_table_new (g_direct_hash, g_direct_equal); + gst_gl_display_glutThread = g_thread_create ( + (GThreadFunc) gst_gl_display_glutThreadFunc, display, TRUE, NULL); + g_cond_wait (display->cond_create, display->mutex); + } + //request glut window creation + else + { + //blocking call because glut context must be alive + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_CREATE, display); + g_cond_wait (display->cond_create, display->mutex); + } + gst_gl_display_unlock (display); +} + + +void +gst_gl_display_lock (GstGLDisplay * display) +{ + g_mutex_lock (display->mutex); +} + + +void +gst_gl_display_unlock (GstGLDisplay * display) +{ + g_mutex_unlock (display->mutex); +} + + +/* Post a message that will be handled by the idle function */ +void +gst_gl_display_postMessage (GstGLDisplayAction action, GstGLDisplay* display) +{ + GstGLDisplayMsg* msg = g_new0 (GstGLDisplayMsg, 1); + msg->action = action; + msg->glutWinId = display->glutWinId; + msg->display = display; + g_async_queue_push (gst_gl_display_messageQueue, msg); +} + + +/* Called by gst_gl elements */ +void +gst_gl_display_setClientReshapeCallback (GstGLDisplay * display, CRCB cb) +{ + gst_gl_display_lock (display); + display->clientReshapeCallback = cb; + gst_gl_display_unlock (display); +} + + +/* Called by gst_gl elements */ +void +gst_gl_display_setClientDrawCallback (GstGLDisplay * display, CDCB cb) +{ + gst_gl_display_lock (display); + display->clientDrawCallback = cb; + gst_gl_display_unlock (display); +} + + +/* Called by gst gl elements */ +void +gst_gl_display_setVisibleWindow (GstGLDisplay * display, gboolean visible) +{ + gst_gl_display_lock (display); + if (display->visible != visible) + { + display->visible = visible; + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_VISIBLE, display); + } + gst_gl_display_unlock (display); +} + + +/* Called by gstglbuffer */ +void +gst_gl_display_textureRequested (GstGLDisplay * display, GstVideoFormat video_format, + gint width, gint height, guint *texture, + guint *texture_u, guint *texture_v) +{ + gst_gl_display_lock (display); + display->requestedVideo_format = video_format; + display->requestedTextureWidth = width; + display->requestedTextureHeight = height; + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_PREPARE, display); + g_cond_wait (display->cond_make, display->mutex); + *texture = display->requestedTexture; + *texture_u = display->requestedTexture_u; + *texture_v = display->requestedTexture_v; + gst_gl_display_unlock (display); +} + + +/* Called by gst_gl elements */ +void +gst_gl_display_textureChanged (GstGLDisplay * display, GstVideoFormat video_format, + GLuint texture, GLuint texture_u, GLuint texture_v, + gint width, gint height, gpointer data) +{ + gst_gl_display_lock (display); + display->candidateTexture = texture; + display->candidateTexture_u = texture_u; + display->candidateTexture_v = texture_v; + display->candidateVideo_format = video_format; + display->candidateTextureWidth = width; + display->candidateTextureHeight = height; + display->candidateData = data; + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_CHANGE, display); + g_cond_wait (display->cond_fill, display->mutex); + gst_gl_display_unlock (display); +} + + +/* Called by gstglbuffer */ +void +gst_gl_display_clearTexture (GstGLDisplay * display, guint texture, + guint texture_u, guint texture_v) +{ + gst_gl_display_lock (display); + display->textureTrash = texture; + display->textureTrash_u = texture_u; + display->textureTrash_v = texture_v; + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_CLEAR, display); + g_cond_wait (display->cond_clear, display->mutex); + gst_gl_display_unlock (display); +} + + +/* Called by gst_gl elements */ +void +gst_gl_display_videoChanged (GstGLDisplay* display, GstVideoFormat video_format, + gpointer data) +{ + gst_gl_display_lock (display); + //data size is aocciated to the glcontext size + display->outputVideo_format = video_format; + display->outputData = data; + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_VIDEO, display); + g_cond_wait (display->cond_video, display->mutex); + gst_gl_display_unlock (display); +} + + +/* Called by gst_gl elements */ +void +gst_gl_display_postRedisplay (GstGLDisplay* display) +{ + gst_gl_display_lock (display); + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_REDISPLAY, display); + gst_gl_display_unlock (display); +} + + +/* Called by gst_gl elements */ +void +gst_gl_display_set_windowId (GstGLDisplay* display, gulong winId) +{ + static gint glheight = 0; + + //g_print ("Display::gst_gl_display_set_windowId\n"); + + gst_gl_display_lock (display); + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_DESTROY, display); + g_cond_wait (display->cond_destroy, display->mutex); + gst_gl_display_unlock (display); + + if (g_hash_table_size (gst_gl_display_map) == 0) + { + g_thread_join (gst_gl_display_glutThread); + g_print ("Glut thread joined when setting winId\n"); + gst_gl_display_glutThread = NULL; + g_async_queue_unref (gst_gl_display_messageQueue); + g_hash_table_unref (gst_gl_display_map); + gst_gl_display_map = NULL; + } + + //init opengl context + gst_gl_display_initGLContext (display, + 50, glheight++ * (display->glcontext_height+50) + 50, + display->glcontext_width, display->glcontext_height, + display->textureFBOWidth, display->textureFBOHeight, + winId, + TRUE); + +} + + +/* glutReshapeFunc callback */ +void +gst_gl_display_onReshape(gint width, gint height) +{ + gint glutWinId = 0; + GstGLDisplay *display = NULL; + + //retrieve the display associated to the glut context + glutWinId = glutGetWindow (); + display = g_hash_table_lookup (gst_gl_display_map, glutWinId); + + //glutGetWindow return 0 if no windows exists, then g_hash_table_lookup return NULL + if (display == NULL) return; + + gst_gl_display_lock (display); + + //check if a client reshape callback is registered + if (display->clientReshapeCallback) + display->clientReshapeCallback(width, height); + + //default reshape + else + { + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, width, 0, height); + glMatrixMode(GL_MODELVIEW); + } + + gst_gl_display_unlock (display); +} + +/* glutDisplayFunc callback */ +void gst_gl_display_draw(void) +{ + gint glutWinId = 0; + GstGLDisplay *display = NULL; + + //retrieve the display associated to the glut context + glutWinId = glutGetWindow (); + display = g_hash_table_lookup (gst_gl_display_map, glutWinId); + + //glutGetWindow return 0 if no windows exists, then g_hash_table_lookup return NULL + if (display == NULL) return; + + //lock the display because gstreamer elements + //(and so the main thread) may modify it + gst_gl_display_lock (display); + + //check if video format has been setup + if (!display->currentVideo_format) + { + gst_gl_display_unlock (display); + return; + } + + //opengl scene + + glUseProgramObjectARB (0); + glDisable (GL_TEXTURE_RECTANGLE_ARB); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0); + + //check if a client draw callback is registered + if (display->clientDrawCallback) + { + gboolean doRedisplay = + display->clientDrawCallback(display->textureFBO, + display->textureFBOWidth, display->textureFBOHeight); + + glFlush(); + glutSwapBuffers(); + + if (doRedisplay) + gst_gl_display_postMessage (GST_GL_DISPLAY_ACTION_REDISPLAY, display); + } + //default opengl scene + else + { + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->textureFBO); + glEnable (GL_TEXTURE_RECTANGLE_ARB); + + glBegin (GL_QUADS); + glTexCoord2i (display->textureFBOWidth, 0); + glVertex2f (1.0f, 1.0f); + glTexCoord2i (0, 0); + glVertex2f (-1.0f, 1.0f); + glTexCoord2i (0, display->textureFBOHeight); + glVertex2f (-1.0f, -1.0f); + glTexCoord2i (display->textureFBOWidth, display->textureFBOHeight); + glVertex2f (1.0f, -1.0f); + glEnd (); + + glDisable(GL_TEXTURE_RECTANGLE_ARB); + + glFlush(); + glutSwapBuffers(); + + }//end default opengl scene + + gst_gl_display_unlock (display); +} + + +/* glutCloseFunc callback */ +void gst_gl_display_onClose (void) +{ + gint glutWinId = 0; + GstGLDisplay* display = NULL; + + //retrieve the display associated to the glut context + glutWinId = glutGetWindow (); + display = g_hash_table_lookup (gst_gl_display_map, glutWinId); + + //glutGetWindow return 0 if no windows exists, then g_hash_table_lookup return NULL + if (display == NULL) return; + + g_print ("on close\n"); + //gst_event_new_eos(); +} + + +/* called by gst_gl_display_glutPrepareTexture (in the glut thread) */ +void gst_gl_display_make_texture (GstGLDisplay * display) +{ + GstGLDisplayTex* tex = NULL; + + //check if there is a tex available in the pool + if (g_queue_get_length (display->texturePool)) + tex = g_queue_pop_head (display->texturePool); + + //one tex is available + if (tex) + display->requestedTexture = tex->texture; + else + glGenTextures (1, &display->requestedTexture); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->requestedTexture); + switch (display->requestedVideo_format) { + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_AYUV: + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, + display->requestedTextureWidth, display->requestedTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + break; + + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_BGR: + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, + display->requestedTextureWidth, display->requestedTextureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + break; + + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE_ALPHA, + display->requestedTextureWidth, display->requestedTextureHeight, + 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + + //one tex is available + if (tex) + display->requestedTexture_u = tex->texture_u; + else + glGenTextures (1, &display->requestedTexture_u); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->requestedTexture_u); + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + display->requestedTextureWidth, display->requestedTextureHeight, + 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + break; + + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, + display->requestedTextureWidth, display->requestedTextureHeight, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + + //one tex is available + if (tex) + display->requestedTexture_u = tex->texture_u; + else + glGenTextures (1, &display->requestedTexture_u); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->requestedTexture_u); + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, + GST_ROUND_UP_2 (display->requestedTextureWidth) / 2, + GST_ROUND_UP_2 (display->requestedTextureHeight) / 2, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + + //one tex is available + if (tex) + display->requestedTexture_v = tex->texture_v; + else + glGenTextures (1, &display->requestedTexture_v); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->requestedTexture_v); + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, + GST_ROUND_UP_2 (display->requestedTextureWidth) / 2, + GST_ROUND_UP_2 (display->requestedTextureHeight) / 2, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + break; + + default: + g_assert_not_reached (); + } + if (tex) + g_free (tex); +} + + +/* called by gst_gl_display_glutUpdateTexture (in the glut thread) */ +void +gst_gl_display_fill_texture (GstGLDisplay * display) +{ + GstVideoFormat video_format = display->candidateVideo_format; + gint width = display->candidateTextureWidth; + gint height = display->candidateTextureHeight; + gpointer data = display->candidateData; + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->candidateTexture); + + switch (video_format) { + case GST_VIDEO_FORMAT_RGBx: + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, + GL_RGBA, GL_UNSIGNED_BYTE, data); + break; + case GST_VIDEO_FORMAT_BGRx: + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, + GL_BGRA, GL_UNSIGNED_BYTE, data); + break; + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_xRGB: + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, data); + break; + case GST_VIDEO_FORMAT_xBGR: + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, data); + break; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->candidateTexture_u); + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, + GST_ROUND_UP_2 (width) / 2, height, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + break; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + { + gint offsetU = 0; + gint offsetV = 0; + + switch (video_format) + { + case GST_VIDEO_FORMAT_I420: + offsetU = 1; + offsetV = 2; + break; + case GST_VIDEO_FORMAT_YV12: + offsetU = 2; + offsetV = 1; + break; + default: + g_assert_not_reached (); + } + + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->candidateTexture_u); + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, + GST_ROUND_UP_2 (width) / 2, GST_ROUND_UP_2 (height) / 2, + GL_LUMINANCE, GL_UNSIGNED_BYTE, + (guint8 *) data + + gst_video_format_get_component_offset (video_format, offsetU, width, height)); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->candidateTexture_v); + glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, + GST_ROUND_UP_2 (width) / 2, GST_ROUND_UP_2 (height) / 2, + GL_LUMINANCE, GL_UNSIGNED_BYTE, + (guint8 *) data + + gst_video_format_get_component_offset (video_format, offsetV, width, height)); + } + break; + default: + g_assert_not_reached (); + } + + //candidate textures can now be used + display->currentTexture = display->candidateTexture; + display->currentTexture_u = display->candidateTexture_u; + display->currentTexture_v = display->candidateTexture_v; + display->currentVideo_format = display->candidateVideo_format; + display->currentTextureWidth = display->candidateTextureWidth; + display->currentTextureHeight = display->candidateTextureHeight; + +} + + +/* called by gst_gl_display_glutUpdateTexture (in the glut thread) */ +void +gst_gl_display_draw_texture (GstGLDisplay* display) +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, display->fbo); + + glPushAttrib(GL_VIEWPORT_BIT); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0.0, display->textureFBOWidth, 0.0, display->textureFBOHeight); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glViewport(0, 0, display->textureFBOWidth, display->textureFBOHeight); + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + switch (display->currentVideo_format) + { + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + { + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + break; + + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + { + gint i=0; + GLhandleARB GLSLProgram_YUY2_UYVY = 0; + + switch (display->currentVideo_format) + { + case GST_VIDEO_FORMAT_YUY2: + GLSLProgram_YUY2_UYVY = display->GLSLProgram_YUY2; + break; + case GST_VIDEO_FORMAT_UYVY: + GLSLProgram_YUY2_UYVY = display->GLSLProgram_UYVY; + break; + default: + g_assert_not_reached (); + } + + glUseProgramObjectARB (GLSLProgram_YUY2_UYVY); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glActiveTextureARB(GL_TEXTURE1_ARB); + i = glGetUniformLocationARB (GLSLProgram_YUY2_UYVY, "UVtex"); + glUniform1iARB (i, 1); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture_u); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glActiveTextureARB(GL_TEXTURE0_ARB); + i = glGetUniformLocationARB (GLSLProgram_YUY2_UYVY, "Ytex"); + glUniform1iARB (i, 0); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + break; + + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + { + gint i=0; + + glUseProgramObjectARB (display->GLSLProgram_I420_YV12); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glActiveTextureARB(GL_TEXTURE1_ARB); + i = glGetUniformLocationARB (display->GLSLProgram_I420_YV12, "Utex"); + glUniform1iARB (i, 1); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture_u); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glActiveTextureARB(GL_TEXTURE2_ARB); + i = glGetUniformLocationARB (display->GLSLProgram_I420_YV12, "Vtex"); + glUniform1iARB (i, 2); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture_v); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glActiveTextureARB(GL_TEXTURE0_ARB); + i = glGetUniformLocationARB (display->GLSLProgram_I420_YV12, "Ytex"); + glUniform1iARB (i, 0); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + break; + + case GST_VIDEO_FORMAT_AYUV: + { + gint i=0; + + glUseProgramObjectARB (display->GLSLProgram_AYUV); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glActiveTextureARB(GL_TEXTURE0_ARB); + i = glGetUniformLocationARB (display->GLSLProgram_AYUV, "tex"); + glUniform1iARB (i, 0); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->currentTexture); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + break; + + default: + g_assert_not_reached (); + + }//end switch display->currentVideo_format + + glBegin (GL_QUADS); + glTexCoord2i (display->currentTextureWidth, 0); + glVertex2f (1.0f, -1.0f); + glTexCoord2i (0, 0); + glVertex2f (-1.0f, -1.0f); + glTexCoord2i (0, display->currentTextureHeight); + glVertex2f (-1.0f, 1.0f); + glTexCoord2i (display->currentTextureWidth, display->currentTextureHeight); + glVertex2f (1.0f, 1.0f); + glEnd (); + + glDrawBuffer(GL_NONE); + + glUseProgramObjectARB (0); + + glDisable(GL_TEXTURE_RECTANGLE_ARB); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopAttrib(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + checkFramebufferStatus(); +} + + +/* called by gst_gl_display_glutUpdateTexture (in the glut thread) */ +void +gst_gl_display_draw_graphic (GstGLDisplay* display) +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, display->graphicFBO); + + glPushAttrib(GL_VIEWPORT_BIT); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(45, (gfloat)display->glcontext_width/(gfloat)display->glcontext_height, 0.1, 100); + //gluOrtho2D(0.0, display->glcontext_width, 0.0, display->glcontext_height); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glViewport(0, 0, display->glcontext_width, display->glcontext_height); + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + + //check if a client draw callback is registered + if (display->clientDrawCallback) + { + display->clientDrawCallback(display->textureFBO, + display->textureFBOWidth, display->textureFBOHeight); + } + else + { + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->textureFBO); + glEnable (GL_TEXTURE_RECTANGLE_ARB); + + glBegin (GL_QUADS); + glTexCoord2i (display->textureFBOWidth, 0); + glVertex2f (1.0f, 1.0f); + glTexCoord2i (0, 0); + glVertex2f (-1.0f, 1.0f); + glTexCoord2i (0, display->textureFBOHeight); + glVertex2f (-1.0f, -1.0f); + glTexCoord2i (display->textureFBOWidth, display->textureFBOHeight); + glVertex2f (1.0f, -1.0f); + glEnd (); + } + + glDrawBuffer(GL_NONE); + + glUseProgramObjectARB (0); + + glDisable(GL_TEXTURE_RECTANGLE_ARB); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopAttrib(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + checkFramebufferStatus(); +} + + +/* called by gst_gl_display_glutUpdateVideo (in the glut thread) */ +void +gst_gl_display_fill_video (GstGLDisplay* display) +{ + + gint width = display->outputWidth; + gint height = display->outputHeight; + GstVideoFormat outputVideo_format = display->outputVideo_format; + gpointer data = display->outputData; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, display->videoFBO); + + glPushAttrib(GL_VIEWPORT_BIT); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(0.0, width, 0.0, height); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glViewport(0, 0, width, height); + + switch (outputVideo_format) + { + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + { + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->graphicTexture); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + } + break; + + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + { + gint i=0; + GLhandleARB GLSLProgram_to_YUY2_UYVY = 0; + + switch (outputVideo_format) + { + case GST_VIDEO_FORMAT_YUY2: + GLSLProgram_to_YUY2_UYVY = display->GLSLProgram_to_YUY2; + break; + case GST_VIDEO_FORMAT_UYVY: + GLSLProgram_to_YUY2_UYVY = display->GLSLProgram_to_UYVY; + break; + default: + g_assert_not_reached (); + } + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgramObjectARB (GLSLProgram_to_YUY2_UYVY); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glActiveTextureARB(GL_TEXTURE0_ARB); + i = glGetUniformLocationARB (GLSLProgram_to_YUY2_UYVY, "tex"); + glUniform1iARB (i, 0); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->graphicTexture); + } + break; + + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + { + gint i=0; + + glDrawBuffers(3, display->multipleRT); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgramObjectARB (display->GLSLProgram_to_I420_YV12); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glActiveTextureARB(GL_TEXTURE0_ARB); + i = glGetUniformLocationARB (display->GLSLProgram_to_I420_YV12, "tex"); + glUniform1iARB (i, 0); + i = glGetUniformLocationARB (display->GLSLProgram_to_I420_YV12, "w"); + glUniform1fARB (i, (gfloat)width); + i = glGetUniformLocationARB (display->GLSLProgram_to_I420_YV12, "h"); + glUniform1fARB (i, (gfloat)height); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->graphicTexture); + } + break; + + case GST_VIDEO_FORMAT_AYUV: + { + gint i=0; + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgramObjectARB (display->GLSLProgram_to_AYUV); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + + glActiveTextureARB(GL_TEXTURE0_ARB); + i = glGetUniformLocationARB (display->GLSLProgram_to_AYUV, "tex"); + glUniform1iARB (i, 0); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->graphicTexture); + } + break; + + default: + g_assert_not_reached (); + + }//end switch display->currentVideo_format + + glBegin (GL_QUADS); + glTexCoord2i (width, 0); + glVertex2f (1.0f, 1.0f); + glTexCoord2i (0, 0); + glVertex2f (-1.0f, 1.0f); + glTexCoord2i (0, height); + glVertex2f (-1.0f, -1.0f); + glTexCoord2i (width, height); + glVertex2f (1.0f, -1.0f); + glEnd (); + + glDrawBuffer(GL_NONE); + + glUseProgramObjectARB (0); + + glDisable(GL_TEXTURE_RECTANGLE_ARB); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopAttrib(); + + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); + + checkFramebufferStatus(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, display->videoFBO); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + + switch (outputVideo_format) { + case GST_VIDEO_FORMAT_RGBx: + glReadPixels (0, 0, width, height, GL_RGBA, + GL_UNSIGNED_BYTE, data); + break; + case GST_VIDEO_FORMAT_BGRx: + glReadPixels (0, 0, width, height, GL_BGRA, + GL_UNSIGNED_BYTE, data); + break; + case GST_VIDEO_FORMAT_xBGR: + glReadPixels (0, 0, width, height, GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, data); + break; + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_xRGB: + glReadPixels (0, 0, width, height, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8, data); + break; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + glReadPixels (0, 0, GST_ROUND_UP_2 (width) / 2, height, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, data); + break; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + { + gint offsetU = 0; + gint offsetV = 0; + + switch (outputVideo_format) + { + case GST_VIDEO_FORMAT_I420: + offsetU = 1; + offsetV = 2; + break; + case GST_VIDEO_FORMAT_YV12: + offsetU = 2; + offsetV = 1; + break; + default: + g_assert_not_reached (); + } + + glReadPixels (0, 0, width, height, GL_LUMINANCE, + GL_UNSIGNED_BYTE, data); + + glReadBuffer(GL_COLOR_ATTACHMENT1_EXT); + glReadPixels (0, 0, GST_ROUND_UP_2 (width) / 2, GST_ROUND_UP_2 (height) / 2, + GL_LUMINANCE, GL_UNSIGNED_BYTE, + (guint8 *) data + + gst_video_format_get_component_offset (outputVideo_format, offsetU, width, height)); + + glReadBuffer(GL_COLOR_ATTACHMENT2_EXT); + glReadPixels (0, 0, GST_ROUND_UP_2 (width) / 2, GST_ROUND_UP_2 (height) / 2, + GL_LUMINANCE, GL_UNSIGNED_BYTE, + (guint8 *) data + + gst_video_format_get_component_offset (outputVideo_format, offsetV, width, height)); + } + break; + default: + g_assert_not_reached (); + } + + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); + checkFramebufferStatus(); +} + + +/* called by gst_gl_display_glutCreateWindow (in the glut thread) */ +GLhandleARB +gst_gl_display_loadGLSLprogram (gchar* textFProgram) +{ + GLhandleARB FHandle = 0; + GLhandleARB PHandle = 0; + gchar s[32768]; + gint i = 0; + + //Set up program objects + PHandle = glCreateProgramObjectARB (); + + //Compile the shader + FHandle = glCreateShaderObjectARB (GL_FRAGMENT_SHADER_ARB); + glShaderSourceARB (FHandle, 1, &textFProgram, NULL); + glCompileShaderARB (FHandle); + + //Print the compilation log + glGetObjectParameterivARB (FHandle, GL_OBJECT_COMPILE_STATUS_ARB, &i); + glGetInfoLogARB (FHandle, sizeof(s)/sizeof(char), NULL, s); + g_print ("Compile Log: %s\n", s); + + //link the shader + glAttachObjectARB (PHandle, FHandle); + glLinkProgramARB (PHandle); + + //Print the link log + glGetInfoLogARB (PHandle, sizeof(s)/sizeof(char), NULL, s); + g_print ("Link Log: %s\n", s); + + return PHandle; +} + + +/* Called by gst_gl_display_fill_video */ +void +checkFramebufferStatus(void) +{ + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + break; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + g_print("GL_FRAMEBUFFER_UNSUPPORTED_EXT\n"); + break; + + default: + g_print("General FBO error\n"); + } +} diff --git a/gst-libs/gst/gl/gstgldisplay.h b/gst-libs/gst/gl/gstgldisplay.h new file mode 100644 index 0000000000..bd0398ffe5 --- /dev/null +++ b/gst-libs/gst/gl/gstgldisplay.h @@ -0,0 +1,195 @@ +#ifndef __GST_GL_H__ +#define __GST_GL_H__ + +#include +#include + +#include +#include + +#define GST_TYPE_GL_DISPLAY \ + (gst_gl_display_get_type()) +#define GST_GL_DISPLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_DISPLAY,GstGLDisplay)) +#define GST_GL_DISPLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GL_DISPLAY,GstGLDisplayClass)) +#define GST_IS_GL_DISPLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_DISPLAY)) +#define GST_IS_GL_DISPLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GL_DISPLAY)) + +typedef struct _GstGLDisplay GstGLDisplay; +typedef struct _GstGLDisplayClass GstGLDisplayClass; + +//Message type +typedef enum { + GST_GL_DISPLAY_ACTION_CREATE, + GST_GL_DISPLAY_ACTION_DESTROY, + GST_GL_DISPLAY_ACTION_VISIBLE, + GST_GL_DISPLAY_ACTION_PREPARE, + GST_GL_DISPLAY_ACTION_CHANGE, + GST_GL_DISPLAY_ACTION_CLEAR, + GST_GL_DISPLAY_ACTION_VIDEO, + GST_GL_DISPLAY_ACTION_REDISPLAY + +} GstGLDisplayAction; + + +//Message to communicate with the glut thread +typedef struct _GstGLDisplayMsg { + GstGLDisplayAction action; + gint glutWinId; + GstGLDisplay* display; +} GstGLDisplayMsg; + + +//Texture pool elements +typedef struct _GstGLDisplayTex { + GLuint texture; + GLuint texture_u; + GLuint texture_v; +} GstGLDisplayTex; + + +//Client callbacks +typedef void (* CRCB) ( GLuint, GLuint ); +typedef gboolean (* CDCB) ( GLuint, GLuint, GLuint); + +struct _GstGLDisplay { + GObject object; + + GMutex *mutex; + + GQueue* texturePool; + + GCond *cond_make; + GCond *cond_fill; + GCond *cond_clear; + GCond *cond_video; + + GCond *cond_create; + GCond *cond_destroy; + gint glutWinId; + gulong winId; + GString *title; + gint win_xpos; + gint win_ypos; + gint glcontext_width; + gint glcontext_height; + gboolean visible; + + //intput frame buffer object (video -> GL) + GLuint fbo; + GLuint depthBuffer; + GLuint textureFBO; + GLuint textureFBOWidth; + GLuint textureFBOHeight; + + //graphic frame buffer object (GL texture -> GL scene) + GLuint graphicFBO; + GLuint graphicDepthBuffer; + GLuint graphicTexture; + + GLuint requestedTexture; + GLuint requestedTexture_u; + GLuint requestedTexture_v; + GstVideoFormat requestedVideo_format; + GLuint requestedTextureWidth; + GLuint requestedTextureHeight; + + GLuint candidateTexture; + GLuint candidateTexture_u; + GLuint candidateTexture_v; + GstVideoFormat candidateVideo_format; + GLuint candidateTextureWidth; + GLuint candidateTextureHeight; + gpointer candidateData; + + GLuint currentTexture; + GLuint currentTexture_u; + GLuint currentTexture_v; + GstVideoFormat currentVideo_format; + GLuint currentTextureWidth; + GLuint currentTextureHeight; + + GLuint textureTrash; + GLuint textureTrash_u; + GLuint textureTrash_v; + + //output frame buffer object (GL -> video) + GLuint videoFBO; + GLuint videoDepthBuffer; + GLuint videoTexture; + GLuint videoTexture_u; + GLuint videoTexture_v; + gint outputWidth; + gint outputHeight; + GstVideoFormat outputVideo_format; + gpointer outputData; + GLenum multipleRT[3]; + + //from video to texture + + gchar* textFProgram_YUY2_UYVY; + GLhandleARB GLSLProgram_YUY2; + GLhandleARB GLSLProgram_UYVY; + + gchar* textFProgram_I420_YV12; + GLhandleARB GLSLProgram_I420_YV12; + + gchar* textFProgram_AYUV; + GLhandleARB GLSLProgram_AYUV; + + //from texture to video + + gchar* textFProgram_to_YUY2_UYVY; + GLhandleARB GLSLProgram_to_YUY2; + GLhandleARB GLSLProgram_to_UYVY; + + gchar* textFProgram_to_I420_YV12; + GLhandleARB GLSLProgram_to_I420_YV12; + + gchar* textFProgram_to_AYUV; + GLhandleARB GLSLProgram_to_AYUV; + + //client callbacks + CRCB clientReshapeCallback; + CDCB clientDrawCallback; +}; + + +struct _GstGLDisplayClass { + GObjectClass object_class; +}; + +GType gst_gl_display_get_type (void); + + +//------------------------------------------------------------ +//-------------------- Public déclarations ------------------ +//------------------------------------------------------------ +GstGLDisplay *gst_gl_display_new (void); +void gst_gl_display_initGLContext (GstGLDisplay* display, + GLint x, GLint y, + GLint graphic_width, GLint graphic_height, + GLint video_width, GLint video_height, + gulong winId, + gboolean visible); +void gst_gl_display_setClientReshapeCallback (GstGLDisplay* display, CRCB cb); +void gst_gl_display_setClientDrawCallback (GstGLDisplay* display, CDCB cb); +void gst_gl_display_setVisibleWindow (GstGLDisplay* display, gboolean visible); +void gst_gl_display_textureRequested (GstGLDisplay* display, GstVideoFormat format, + gint width, gint height, guint* texture, + guint* texture_u, guint* texture_v); +void gst_gl_display_textureChanged (GstGLDisplay* display, GstVideoFormat video_format, + GLuint texture, GLuint texture_u, GLuint texture_v, + gint width, gint height, gpointer data); +void gst_gl_display_clearTexture (GstGLDisplay* display, guint texture, + guint texture_u, guint texture_v); + +void gst_gl_display_videoChanged (GstGLDisplay* display, GstVideoFormat video_format, + gpointer data); +void gst_gl_display_postRedisplay (GstGLDisplay* display); +void gst_gl_display_set_windowId (GstGLDisplay* display, gulong winId); + +#endif diff --git a/gst/gl/gstgl.c b/gst/gl/gstgl.c new file mode 100644 index 0000000000..3fb06791d7 --- /dev/null +++ b/gst/gl/gstgl.c @@ -0,0 +1,42 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* include gl filters headers */ +#include "gstglgraphicmaker.h" +#include "gstglvideomaker.h" + +#include "gstglimagesink.h" + +#define GST_CAT_DEFAULT gst_gl_gstgl_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +/* Register filters that make up the gstgl plugin */ +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_gl_gstgl_debug, "gstgl", 0, "gstgl"); + + if (!gst_element_register (plugin, "glvideomaker", + GST_RANK_NONE, GST_TYPE_GL_VIDEOMAKER)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glgraphicmaker", + GST_RANK_NONE, GST_TYPE_GL_GRAPHICMAKER)) { + return FALSE; + } + + if (!gst_element_register (plugin, "glimagesink", + GST_RANK_NONE, GST_TYPE_GLIMAGE_SINK)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "gstgl", + "OpenGL plugin", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/gl/gstglgraphicmaker.c b/gst/gl/gstglgraphicmaker.c new file mode 100644 index 0000000000..2a40906370 --- /dev/null +++ b/gst/gl/gstglgraphicmaker.c @@ -0,0 +1,419 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglgraphicmaker.h" + + +#define GST_CAT_DEFAULT gst_gl_graphicmaker_debug + GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +/* inpect details */ +static const GstElementDetails element_details = { + "glgraphicmaker", + "Transform filter", + "output an opengl scene flux", + "Jhonny Bravo and Kelly" + }; + +/* Source pad definition */ +static GstStaticPadTemplate gst_gl_graphicmaker_src_pad_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_GL_VIDEO_CAPS) + ); + +/* Source pad definition */ +static GstStaticPadTemplate gst_gl_graphicmaker_sink_pad_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBx ";" + GST_VIDEO_CAPS_BGRx ";" + GST_VIDEO_CAPS_xRGB ";" + GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_YUV ("{ I420, YV12, YUY2, UYVY, AYUV }")) + ); + +/* Properties */ +enum +{ + PROP_0, + PROP_GLCONTEXT_WIDTH, + PROP_GLCONTEXT_HEIGHT, + PROP_CLIENT_RESHAPE_CALLBACK, + PROP_CLIENT_DRAW_CALLBACK +}; + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_gl_graphicmaker_debug, "glgraphicmaker", 0, "glgraphicmaker element"); + +GST_BOILERPLATE_FULL (GstGLGraphicmaker, gst_gl_graphicmaker, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + +static void gst_gl_graphicmaker_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_graphicmaker_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_graphicmaker_reset (GstGLGraphicmaker* graphicmaker); +static gboolean gst_gl_graphicmaker_set_caps (GstBaseTransform * bt, + GstCaps * incaps, GstCaps * outcaps); +static GstCaps *gst_gl_graphicmaker_transform_caps (GstBaseTransform * bt, + GstPadDirection direction, GstCaps * caps); +static gboolean gst_gl_graphicmaker_start (GstBaseTransform * bt); +static gboolean gst_gl_graphicmaker_stop (GstBaseTransform * bt); +static GstFlowReturn gst_gl_graphicmaker_prepare_output_buffer (GstBaseTransform * + trans, GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf); +static GstFlowReturn gst_gl_graphicmaker_transform (GstBaseTransform * trans, + GstBuffer * inbuf, GstBuffer * outbuf); +static gboolean gst_gl_graphicmaker_get_unit_size (GstBaseTransform * trans, + GstCaps * caps, guint * size); + + +static void +gst_gl_graphicmaker_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details (element_class, &element_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_gl_graphicmaker_src_pad_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_gl_graphicmaker_sink_pad_template)); +} + +static void +gst_gl_graphicmaker_class_init (GstGLGraphicmakerClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + gobject_class->set_property = gst_gl_graphicmaker_set_property; + gobject_class->get_property = gst_gl_graphicmaker_get_property; + + GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = + gst_gl_graphicmaker_transform_caps; + GST_BASE_TRANSFORM_CLASS (klass)->transform = gst_gl_graphicmaker_transform; + GST_BASE_TRANSFORM_CLASS (klass)->start = gst_gl_graphicmaker_start; + GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_graphicmaker_stop; + GST_BASE_TRANSFORM_CLASS (klass)->set_caps = gst_gl_graphicmaker_set_caps; + GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = gst_gl_graphicmaker_get_unit_size; + GST_BASE_TRANSFORM_CLASS (klass)->prepare_output_buffer = + gst_gl_graphicmaker_prepare_output_buffer; + + + g_object_class_install_property (gobject_class, PROP_GLCONTEXT_WIDTH, + g_param_spec_int ("glcontext_width", "OpenGL context width", + "Change the opengl context width", 0, INT_MAX, 0, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_GLCONTEXT_HEIGHT, + g_param_spec_int ("glcontext_height", "OpenGL context height", + "Change the opengl context height", 0, INT_MAX, 0, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_RESHAPE_CALLBACK, + g_param_spec_pointer ("client_reshape_callback", "Client reshape callback", + "Executed in next glut loop iteration when window size is changed", + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_CLIENT_DRAW_CALLBACK, + g_param_spec_pointer ("client_draw_callback", "Client draw callback", + "Executed in next glut loop iteration when glutPostRedisplay is called", + G_PARAM_WRITABLE)); +} + +static void +gst_gl_graphicmaker_init (GstGLGraphicmaker* graphicmaker, GstGLGraphicmakerClass * klass) +{ + gst_gl_graphicmaker_reset (graphicmaker); +} + +static void +gst_gl_graphicmaker_set_property (GObject* object, guint prop_id, + const GValue* value, GParamSpec* pspec) +{ + GstGLGraphicmaker* graphicmaker = GST_GL_GRAPHICMAKER (object); + + switch (prop_id) { + case PROP_GLCONTEXT_WIDTH: + { + graphicmaker->glcontext_width = g_value_get_int (value); + break; + } + case PROP_GLCONTEXT_HEIGHT: + { + graphicmaker->glcontext_height = g_value_get_int (value); + break; + } + case PROP_CLIENT_RESHAPE_CALLBACK: + { + graphicmaker->clientReshapeCallback = g_value_get_pointer (value); + break; + } + case PROP_CLIENT_DRAW_CALLBACK: + { + graphicmaker->clientDrawCallback = g_value_get_pointer (value); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_graphicmaker_get_property (GObject* object, guint prop_id, + GValue* value, GParamSpec* pspec) +{ + //GstGLGraphicmaker *graphicmaker = GST_GL_GRAPHICMAKER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_graphicmaker_reset (GstGLGraphicmaker* graphicmaker) +{ + if (graphicmaker->display) { + g_object_unref (graphicmaker->display); + graphicmaker->display = NULL; + } + graphicmaker->peek = FALSE; + + graphicmaker->glcontext_width = 0; + graphicmaker->glcontext_height = 0; + graphicmaker->clientReshapeCallback = NULL; + graphicmaker->clientDrawCallback = NULL; +} + +static gboolean +gst_gl_graphicmaker_start (GstBaseTransform * bt) +{ + GstGLGraphicmaker* graphicmaker = GST_GL_GRAPHICMAKER (bt); + + return TRUE; +} + +static gboolean +gst_gl_graphicmaker_stop (GstBaseTransform * bt) +{ + GstGLGraphicmaker* graphicmaker = GST_GL_GRAPHICMAKER (bt); + + gst_gl_graphicmaker_reset (graphicmaker); + + return TRUE; +} + +static GstCaps * +gst_gl_graphicmaker_transform_caps (GstBaseTransform * bt, + GstPadDirection direction, GstCaps * caps) +{ + GstGLGraphicmaker* graphicmaker; + GstStructure* structure; + GstCaps* newcaps, *newothercaps; + GstStructure* newstruct; + const GValue* width_value; + const GValue* height_value; + const GValue* framerate_value; + const GValue* par_value; + + graphicmaker = GST_GL_GRAPHICMAKER (bt); + + GST_ERROR ("transform caps %" GST_PTR_FORMAT, caps); + + structure = gst_caps_get_structure (caps, 0); + + width_value = gst_structure_get_value (structure, "width"); + height_value = gst_structure_get_value (structure, "height"); + framerate_value = gst_structure_get_value (structure, "framerate"); + par_value = gst_structure_get_value (structure, "pixel-aspect-ratio"); + + if (direction == GST_PAD_SRC) + { + newothercaps = gst_caps_new_simple ("video/x-raw-rgb", NULL); + newstruct = gst_caps_get_structure (newothercaps, 0); + gst_structure_set_value (newstruct, "width", width_value); + gst_structure_set_value (newstruct, "height", height_value); + gst_structure_set_value (newstruct, "framerate", framerate_value); + if (par_value) + gst_structure_set_value (newstruct, "pixel-aspect-ratio", par_value); + else + gst_structure_set (newstruct, "pixel-aspect-ratio", GST_TYPE_FRACTION, + 1, 1, NULL); + newcaps = gst_caps_new_simple ("video/x-raw-yuv", NULL); + gst_caps_append(newcaps, newothercaps); + newstruct = gst_caps_get_structure (newcaps, 0); + gst_structure_set_value (newstruct, "width", width_value); + gst_structure_set_value (newstruct, "height", height_value); + } + else + { + newcaps = gst_caps_new_simple ("video/x-raw-gl", NULL); + newstruct = gst_caps_get_structure (newcaps, 0); + if (graphicmaker->glcontext_width != 0 && graphicmaker->glcontext_height != 0) + { + GValue value_w = { 0 }; + GValue value_h = { 0 }; + g_value_init (&value_w, G_TYPE_INT); + g_value_init (&value_h, G_TYPE_INT); + g_value_set_int (&value_w, graphicmaker->glcontext_width); + g_value_set_int (&value_h, graphicmaker->glcontext_height); + gst_structure_set_value (newstruct, "width", &value_w); + gst_structure_set_value (newstruct, "height", &value_h); + g_value_unset (&value_w); + g_value_unset (&value_h); + } + else + { + gst_structure_set_value (newstruct, "width", width_value); + gst_structure_set_value (newstruct, "height", height_value); + } + } + + + gst_structure_set_value (newstruct, "framerate", framerate_value); + if (par_value) + gst_structure_set_value (newstruct, "pixel-aspect-ratio", par_value); + else + gst_structure_set (newstruct, "pixel-aspect-ratio", GST_TYPE_FRACTION, + 1, 1, NULL); + + GST_ERROR ("new caps %" GST_PTR_FORMAT, newcaps); + + return newcaps; +} + +static gboolean +gst_gl_graphicmaker_set_caps (GstBaseTransform* bt, GstCaps* incaps, + GstCaps* outcaps) +{ + GstGLGraphicmaker* graphicmaker; + gboolean ret; + + graphicmaker = GST_GL_GRAPHICMAKER (bt); + + GST_DEBUG ("called with %" GST_PTR_FORMAT, incaps); + + ret = gst_video_format_parse_caps (incaps, &graphicmaker->video_format, + &graphicmaker->width, &graphicmaker->height); + + if (!ret) { + GST_DEBUG ("bad caps"); + return FALSE; + } + + graphicmaker->display = gst_gl_display_new (); + + //client opengl context size + if (graphicmaker->glcontext_width != 0 && graphicmaker->glcontext_height != 0) + gst_gl_display_initGLContext (graphicmaker->display, 0, 0, + graphicmaker->glcontext_width, graphicmaker->glcontext_height, + graphicmaker->width, graphicmaker->height, 0, FALSE); + //default opengl context size + else + { + //init unvisible opengl context + static gint glcontext_y = 0; + gst_gl_display_initGLContext (graphicmaker->display, + 50, glcontext_y++ * (graphicmaker->height+50) + 50, + graphicmaker->width, graphicmaker->height, + graphicmaker->width, graphicmaker->height, 0, FALSE); + } + + //set the client reshape callback + gst_gl_display_setClientReshapeCallback (graphicmaker->display, + graphicmaker->clientReshapeCallback); + + //set the client draw callback + gst_gl_display_setClientDrawCallback (graphicmaker->display, + graphicmaker->clientDrawCallback); + + return ret; +} + +static gboolean +gst_gl_graphicmaker_get_unit_size (GstBaseTransform * trans, GstCaps * caps, + guint * size) +{ + gboolean ret; + GstStructure *structure; + gint width; + gint height; + + structure = gst_caps_get_structure (caps, 0); + if (gst_structure_has_name (structure, "video/x-raw-gl")) + { + GstVideoFormat video_format; + + ret = gst_gl_buffer_format_parse_caps (caps, &video_format, &width, &height); + if (ret) + *size = gst_gl_buffer_format_get_size (video_format, width, height); + } + else + { + GstVideoFormat video_format; + + ret = gst_video_format_parse_caps (caps, &video_format, &width, &height); + if (ret) + *size = gst_video_format_get_size (video_format, width, height); + } + + return TRUE; +} + +static GstFlowReturn +gst_gl_graphicmaker_prepare_output_buffer (GstBaseTransform* trans, + GstBuffer* input, gint size, GstCaps* caps, GstBuffer** buf) +{ + GstGLGraphicmaker* graphicmaker; + GstGLBuffer* gl_outbuf; + + graphicmaker = GST_GL_GRAPHICMAKER (trans); + + //blocking call + + //client opengl context size + if (graphicmaker->glcontext_width != 0 && graphicmaker->glcontext_height != 0) + gl_outbuf = gst_gl_buffer_new_from_video_format (graphicmaker->display, + graphicmaker->video_format, + graphicmaker->glcontext_width, graphicmaker->glcontext_height, + graphicmaker->width, graphicmaker->height); + //default opengl context size + else + gl_outbuf = gst_gl_buffer_new_from_video_format (graphicmaker->display, + graphicmaker->video_format, + graphicmaker->width, graphicmaker->height, + graphicmaker->width, graphicmaker->height); + *buf = GST_BUFFER (gl_outbuf); + gst_buffer_set_caps (*buf, caps); + + return GST_FLOW_OK; +} + + +static GstFlowReturn +gst_gl_graphicmaker_transform (GstBaseTransform* trans, GstBuffer* inbuf, + GstBuffer* outbuf) +{ + GstGLGraphicmaker* graphicmaker; + GstGLBuffer* gl_outbuf = GST_GL_BUFFER (outbuf); + + graphicmaker = GST_GL_GRAPHICMAKER (trans); + + GST_DEBUG ("making graphic %p size %d", + GST_BUFFER_DATA (inbuf), GST_BUFFER_SIZE (inbuf)); + + //blocking call + gst_gl_display_textureChanged(graphicmaker->display, graphicmaker->video_format, + gl_outbuf->texture, gl_outbuf->texture_u, gl_outbuf->texture_v, + gl_outbuf->width, gl_outbuf->height, GST_BUFFER_DATA (inbuf)); + + return GST_FLOW_OK; +} diff --git a/gst/gl/gstglgraphicmaker.h b/gst/gl/gstglgraphicmaker.h new file mode 100644 index 0000000000..7c332a6ba5 --- /dev/null +++ b/gst/gl/gstglgraphicmaker.h @@ -0,0 +1,52 @@ +#ifndef _GST_GLGRAPHICMAKER_H_ +#define _GST_GLGRAPHICMAKER_H_ + +#include +#include +#include + +#include "gstglbuffer.h" + +G_BEGIN_DECLS + +#define GST_TYPE_GL_GRAPHICMAKER (gst_gl_graphicmaker_get_type()) +#define GST_GL_GRAPHICMAKER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_GRAPHICMAKER,GstGLGraphicmaker)) +#define GST_IS_GL_GRAPHICMAKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_GRAPHICMAKER)) +#define GST_GL_GRAPHICMAKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_GRAPHICMAKER,GstGLGraphicmakerClass)) +#define GST_IS_GL_GRAPHICMAKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_GRAPHICMAKER)) +#define GST_GL_GRAPHICMAKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_GRAPHICMAKER,GstGLGraphicmakerClass)) + +typedef struct _GstGLGraphicmaker GstGLGraphicmaker; +typedef struct _GstGLGraphicmakerClass GstGLGraphicmakerClass; + + +struct _GstGLGraphicmaker +{ + GstBaseTransform base_transform; + + GstPad *srcpad; + GstPad *sinkpad; + + GstGLDisplay *display; + GstVideoFormat video_format; + gint width; + gint height; + + gboolean peek; + + gint glcontext_width; + gint glcontext_height; + CRCB clientReshapeCallback; + CDCB clientDrawCallback; +}; + +struct _GstGLGraphicmakerClass +{ + GstBaseTransformClass base_transform_class; +}; + +GType gst_gl_graphicmaker_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLGRAPHICMAKER_H_ */ diff --git a/gst/gl/gstglimagesink.c b/gst/gl/gstglimagesink.c new file mode 100644 index 0000000000..75cc4f0680 --- /dev/null +++ b/gst/gl/gstglimagesink.c @@ -0,0 +1,517 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstglimagesink.h" + +GST_DEBUG_CATEGORY (gst_debug_glimage_sink); +#define GST_CAT_DEFAULT gst_debug_glimage_sink + +static void gst_glimage_sink_init_interfaces (GType type); + +static void gst_glimage_sink_finalize (GObject * object); +static void gst_glimage_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * param_spec); +static void gst_glimage_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * param_spec); + +static GstStateChangeReturn +gst_glimage_sink_change_state (GstElement * element, GstStateChange transition); + +static void gst_glimage_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end); +static GstCaps *gst_glimage_sink_get_caps (GstBaseSink * bsink); +static gboolean gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static GstFlowReturn gst_glimage_sink_render (GstBaseSink * bsink, + GstBuffer * buf); +static gboolean gst_glimage_sink_start (GstBaseSink * bsink); +static gboolean gst_glimage_sink_stop (GstBaseSink * bsink); +static gboolean gst_glimage_sink_unlock (GstBaseSink * bsink); + +static void gst_glimage_sink_xoverlay_init (GstXOverlayClass * iface); +static void gst_glimage_sink_set_xwindow_id (GstXOverlay * overlay, + gulong window_id); +static void gst_glimage_sink_expose (GstXOverlay * overlay); +static gboolean gst_glimage_sink_interface_supported (GstImplementsInterface * + iface, GType type); +static void gst_glimage_sink_implements_init (GstImplementsInterfaceClass * + klass); + +static const GstElementDetails gst_glimage_sink_details = +GST_ELEMENT_DETAILS ("OpenGL video sink", + "Sink/Video", + "A videosink based on OpenGL", + "Julien Isorce "); + +static GstStaticPadTemplate gst_glimage_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_GL_VIDEO_CAPS ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";" + GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_YUV ("{ YUY2, UYVY, AYUV, YV12, I420 }")) + ); + +enum +{ + ARG_0, + ARG_DISPLAY +}; + +GST_BOILERPLATE_FULL (GstGLImageSink, gst_glimage_sink, GstVideoSink, + GST_TYPE_VIDEO_SINK, gst_glimage_sink_init_interfaces); + +static void +gst_glimage_sink_init_interfaces (GType type) +{ + + static const GInterfaceInfo implements_info = { + (GInterfaceInitFunc) gst_glimage_sink_implements_init, + NULL, + NULL + }; + + static const GInterfaceInfo xoverlay_info = { + (GInterfaceInitFunc) gst_glimage_sink_xoverlay_init, + NULL, + NULL, + }; + + g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, + &implements_info); + + g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info); + +} + +static void +gst_glimage_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_glimage_sink_details); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_glimage_sink_template)); + +} + +static void +gst_glimage_sink_class_init (GstGLImageSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + gobject_class->set_property = gst_glimage_sink_set_property; + gobject_class->get_property = gst_glimage_sink_get_property; + + g_object_class_install_property (gobject_class, ARG_DISPLAY, + g_param_spec_string ("display", "Display", "Display name", + NULL, G_PARAM_READWRITE)); + + gobject_class->finalize = gst_glimage_sink_finalize; + + gstelement_class->change_state = gst_glimage_sink_change_state; + + gstbasesink_class->set_caps = gst_glimage_sink_set_caps; + gstbasesink_class->get_times = gst_glimage_sink_get_times; + gstbasesink_class->preroll = gst_glimage_sink_render; + gstbasesink_class->render = gst_glimage_sink_render; + gstbasesink_class->start = gst_glimage_sink_start; + gstbasesink_class->stop = gst_glimage_sink_stop; + gstbasesink_class->unlock = gst_glimage_sink_unlock; +} + +static void +gst_glimage_sink_init (GstGLImageSink * glimage_sink, + GstGLImageSinkClass * glimage_sink_class) +{ + glimage_sink->display_name = NULL; + glimage_sink->window_id = 0; + glimage_sink->display = NULL; + glimage_sink->stored_buffer = NULL; +} + +static void +gst_glimage_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLImageSink *glimage_sink; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (object)); + + glimage_sink = GST_GLIMAGE_SINK (object); + + switch (prop_id) { + case ARG_DISPLAY: + g_free (glimage_sink->display_name); + glimage_sink->display_name = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_glimage_sink_finalize (GObject * object) +{ + GstGLImageSink *glimage_sink; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (object)); + + glimage_sink = GST_GLIMAGE_SINK (object); + + if (glimage_sink->caps) { + gst_caps_unref (glimage_sink->caps); + } + g_free (glimage_sink->display_name); +} + +static void +gst_glimage_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLImageSink *glimage_sink; + + g_return_if_fail (GST_IS_GLIMAGE_SINK (object)); + + glimage_sink = GST_GLIMAGE_SINK (object); + + switch (prop_id) { + case ARG_DISPLAY: + g_value_set_string (value, glimage_sink->display_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* + * GstElement methods + */ + +static GstStateChangeReturn +gst_glimage_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstGLImageSink *glimage_sink; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + GST_DEBUG ("change state"); + + glimage_sink = GST_GLIMAGE_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* FIXME clear window */ + glimage_sink->fps_n = 0; + glimage_sink->fps_d = 1; + GST_VIDEO_SINK_WIDTH (glimage_sink) = 0; + GST_VIDEO_SINK_HEIGHT (glimage_sink) = 0; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + /* FIXME dispose of window */ + break; + default: + break; + } + + return ret; +} + +/* + * GstBaseSink methods + */ + +static gboolean +gst_glimage_sink_start (GstBaseSink * bsink) +{ + GstGLImageSink *glimage_sink; + + GST_DEBUG ("start"); + + glimage_sink = GST_GLIMAGE_SINK (bsink); + + /*if (glimage_sink->display && glimage_sink->window_id) { + gst_gl_display_set_window (glimage_sink->display, glimage_sink->window_id); + gst_gl_display_set_visible (glimage_sink->display, TRUE); + }*/ + + GST_DEBUG ("start done"); + + return TRUE; +} + +static gboolean +gst_glimage_sink_stop (GstBaseSink * bsink) +{ + GstGLImageSink *glimage_sink; + + GST_DEBUG ("stop"); + + glimage_sink = GST_GLIMAGE_SINK (bsink); + + if (glimage_sink->stored_buffer) { + gst_buffer_unref (glimage_sink->stored_buffer); + glimage_sink->stored_buffer = NULL; + } + if (glimage_sink->display) { + gst_gl_display_setVisibleWindow (glimage_sink->display, FALSE); + g_object_unref (glimage_sink->display); + glimage_sink->display = NULL; + } + + return TRUE; +} + +static gboolean +gst_glimage_sink_unlock (GstBaseSink * bsink) +{ + //GstGLImageSink *glimage_sink; + + GST_DEBUG ("unlock"); + + //glimage_sink = GST_GLIMAGE_SINK (bsink); + + /* FIXME */ + + return TRUE; +} + +static void +gst_glimage_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end) +{ + GstGLImageSink *glimagesink; + + glimagesink = GST_GLIMAGE_SINK (bsink); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + *start = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) { + *end = *start + GST_BUFFER_DURATION (buf); + } else { + if (glimagesink->fps_n > 0) { + *end = *start + + gst_util_uint64_scale_int (GST_SECOND, glimagesink->fps_d, + glimagesink->fps_n); + } + } + } +} + + +static gboolean +gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstGLImageSink *glimage_sink; + gint width; + gint height; + gboolean ok; + gint fps_n, fps_d; + gint par_n, par_d; + GstVideoFormat format; + GstStructure *structure; + gboolean is_gl; + + GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); + + glimage_sink = GST_GLIMAGE_SINK (bsink); + + structure = gst_caps_get_structure (caps, 0); + if (gst_structure_has_name (structure, "video/x-raw-gl")) { + is_gl = TRUE; + format = GST_VIDEO_FORMAT_UNKNOWN; + ok = gst_structure_get_int (structure, "width", &width); + ok &= gst_structure_get_int (structure, "height", &height); + } else { + is_gl = FALSE; + ok = gst_video_format_parse_caps (caps, &format, &width, &height); + } + ok &= gst_video_parse_caps_framerate (caps, &fps_n, &fps_d); + ok &= gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d); + + if (!ok) + return FALSE; + + GST_VIDEO_SINK_WIDTH (glimage_sink) = width; + GST_VIDEO_SINK_HEIGHT (glimage_sink) = height; + glimage_sink->is_gl = is_gl; + glimage_sink->format = format; + glimage_sink->width = width; + glimage_sink->height = height; + glimage_sink->fps_n = fps_n; + glimage_sink->fps_d = fps_d; + glimage_sink->par_n = par_n; + glimage_sink->par_d = par_d; + + return TRUE; +} + +static GstFlowReturn +gst_glimage_sink_render (GstBaseSink * bsink, GstBuffer * buf) +{ + GstGLImageSink *glimage_sink = NULL; + GstGLBuffer *gl_buffer = NULL; + + glimage_sink = GST_GLIMAGE_SINK (bsink); + + //is gl + if (glimage_sink->is_gl) + { + //increment gl buffer ref before storage + gl_buffer = GST_GL_BUFFER (gst_buffer_ref (buf)); + + //if glimagesink has not the display yet + if (glimage_sink->display == NULL) + { + glimage_sink->display = g_object_ref (gl_buffer->display); + + gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (glimage_sink)); + + if (glimage_sink->window_id) + gst_gl_display_set_windowId (glimage_sink->display, glimage_sink->window_id); + + gst_gl_display_setVisibleWindow (glimage_sink->display, TRUE); + } + } + //is not gl + else + { + //if glimagesink has not the display yet + if (glimage_sink->display == NULL) + { + + //do not stack when multiple windows + static gint glheight = 0; + + //create a display + glimage_sink->display = gst_gl_display_new (); + + //Notify application to set window id now + if (!glimage_sink->window_id) + gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (glimage_sink)); + + //init opengl context + gst_gl_display_initGLContext (glimage_sink->display, + 50, glheight++ * (glimage_sink->height+50) + 50, + glimage_sink->width, glimage_sink->height, + glimage_sink->width, glimage_sink->height, + glimage_sink->window_id, + TRUE); + } + + //blocking call + gl_buffer = gst_gl_buffer_new_from_video_format (glimage_sink->display, + glimage_sink->format, glimage_sink->width, glimage_sink->height, + glimage_sink->width, glimage_sink->height); + + //blocking call + gst_gl_display_textureChanged(glimage_sink->display, glimage_sink->format, + gl_buffer->texture, gl_buffer->texture_u, gl_buffer->texture_v, + gl_buffer->width, gl_buffer->height, GST_BUFFER_DATA (buf)); + + //gl_buffer is created in this block, so the gl buffer is already referenced + } + + //the buffer is cleared when an other comes in + if (glimage_sink->stored_buffer) + { + gst_buffer_unref (glimage_sink->stored_buffer); + glimage_sink->stored_buffer = NULL; + } + + //store current buffer + glimage_sink->stored_buffer = gl_buffer; + + //redisplay opengl scene + gst_gl_display_postRedisplay (glimage_sink->display); + + return GST_FLOW_OK; +} + + +static void +gst_glimage_sink_xoverlay_init (GstXOverlayClass* iface) +{ + iface->set_xwindow_id = gst_glimage_sink_set_xwindow_id; + iface->expose = gst_glimage_sink_expose; +} + + +static void +gst_glimage_sink_set_xwindow_id (GstXOverlay* overlay, gulong window_id) +{ + GstGLImageSink* glimage_sink = GST_GLIMAGE_SINK (overlay); + + g_return_if_fail (GST_IS_GLIMAGE_SINK (overlay)); + g_assert (glimage_sink->display != NULL); + + GST_DEBUG ("set_xwindow_id %ld", window_id); + + if (glimage_sink->window_id == window_id) + return; + + if (window_id) + { + //if (glimage_sink->window_id && ddrawsink->isInternal) + //close internal window + + glimage_sink->window_id = window_id; + //gst_gl_display_set_windowId (glimage_sink->display, glimage_sink->window_id); + } + + //Handle the case where window_id is 0 and we want the sink to + //create a new window when playback was already started (after set_caps) +} + + +static void +gst_glimage_sink_expose (GstXOverlay * overlay) +{ + GstGLImageSink* glimage_sink = GST_GLIMAGE_SINK (overlay); + + //redisplay opengl scene + if (glimage_sink->display) + gst_gl_display_postRedisplay (glimage_sink->display); +} + + +static gboolean +gst_glimage_sink_interface_supported (GstImplementsInterface * iface, + GType type) +{ + return TRUE; +} + + +static void +gst_glimage_sink_implements_init (GstImplementsInterfaceClass * klass) +{ + klass->supported = gst_glimage_sink_interface_supported; +} diff --git a/gst/gl/gstglimagesink.h b/gst/gl/gstglimagesink.h new file mode 100644 index 0000000000..0d393b9fd6 --- /dev/null +++ b/gst/gl/gstglimagesink.h @@ -0,0 +1,59 @@ +#ifndef _GLIMAGESINK_H_ +#define _GLIMAGESINK_H_ + +#include +#include +#include + +#include "gstglbuffer.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_debug_glimage_sink); + +#define GST_TYPE_GLIMAGE_SINK \ + (gst_glimage_sink_get_type()) +#define GST_GLIMAGE_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GLIMAGE_SINK,GstGLImageSink)) +#define GST_GLIMAGE_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GLIMAGE_SINK,GstGLImageSinkClass)) +#define GST_IS_GLIMAGE_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GLIMAGE_SINK)) +#define GST_IS_GLIMAGE_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GLIMAGE_SINK)) + +typedef struct _GstGLImageSink GstGLImageSink; +typedef struct _GstGLImageSinkClass GstGLImageSinkClass; + +struct _GstGLImageSink +{ + GstVideoSink video_sink; + + //properties + gchar *display_name; + + gulong window_id; + //gboolean isInternal; + + //caps + GstCaps *caps; + GstVideoFormat format; + gint width; + gint height; + gboolean is_gl; + gint fps_n, fps_d; + gint par_n, par_d; + + GstGLDisplay *display; + GstGLBuffer *stored_buffer; + +}; + +struct _GstGLImageSinkClass +{ + GstVideoSinkClass video_sink_class; + +}; + +GType gst_glimage_sink_get_type(void); + +#endif + diff --git a/gst/gl/gstglvideomaker.c b/gst/gl/gstglvideomaker.c new file mode 100644 index 0000000000..9db292b910 --- /dev/null +++ b/gst/gl/gstglvideomaker.c @@ -0,0 +1,289 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglvideomaker.h" + +#define GST_CAT_DEFAULT gst_gl_videomaker_debug + GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static const GstElementDetails element_details = GST_ELEMENT_DETAILS ("FIXME", + "Filter/Effect", + "FIXME example filter", + "FIXME "); + +static GstStaticPadTemplate gst_gl_videomaker_src_pad_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB ";" + GST_VIDEO_CAPS_RGBx ";" + GST_VIDEO_CAPS_BGRx ";" + GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_YUV ("{ I420, YV12, YUY2, UYVY, AYUV }")) + ); + +static GstStaticPadTemplate gst_gl_videomaker_sink_pad_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_GL_VIDEO_CAPS) + ); + +enum +{ + PROP_0 +}; + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_gl_videomaker_debug, "glvideomaker", 0, "glvideomaker element"); + +GST_BOILERPLATE_FULL (GstGLVideomaker, gst_gl_videomaker, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + +static void gst_gl_videomaker_set_property (GObject* object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_videomaker_get_property (GObject* object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_gl_videomaker_reset (GstGLVideomaker* videomaker); +static gboolean gst_gl_videomaker_set_caps (GstBaseTransform* bt, + GstCaps* incaps, GstCaps* outcaps); +static GstCaps* gst_gl_videomaker_transform_caps (GstBaseTransform* bt, + GstPadDirection direction, GstCaps* caps); +static gboolean gst_gl_videomaker_start (GstBaseTransform* bt); +static gboolean gst_gl_videomaker_stop (GstBaseTransform* bt); +static GstFlowReturn gst_gl_videomaker_transform (GstBaseTransform* trans, + GstBuffer* inbuf, GstBuffer* outbuf); +static gboolean +gst_gl_videomaker_get_unit_size (GstBaseTransform* trans, GstCaps* caps, + guint* size); + + +static void +gst_gl_videomaker_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details (element_class, &element_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_gl_videomaker_src_pad_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_gl_videomaker_sink_pad_template)); +} + + +static void +gst_gl_videomaker_class_init (GstGLVideomakerClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + gobject_class->set_property = gst_gl_videomaker_set_property; + gobject_class->get_property = gst_gl_videomaker_get_property; + + GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = + gst_gl_videomaker_transform_caps; + GST_BASE_TRANSFORM_CLASS (klass)->transform = gst_gl_videomaker_transform; + GST_BASE_TRANSFORM_CLASS (klass)->start = gst_gl_videomaker_start; + GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_videomaker_stop; + GST_BASE_TRANSFORM_CLASS (klass)->set_caps = gst_gl_videomaker_set_caps; + GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = + gst_gl_videomaker_get_unit_size; +} + + +static void +gst_gl_videomaker_init (GstGLVideomaker* videomaker, GstGLVideomakerClass* klass) +{ + gst_gl_videomaker_reset (videomaker); +} + + +static void +gst_gl_videomaker_set_property (GObject* object, guint prop_id, + const GValue* value, GParamSpec* pspec) +{ + //GstGLVideomaker *videomaker = GST_GL_VIDEOMAKER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_gl_videomaker_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + //GstGLVideomaker *videomaker = GST_GL_VIDEOMAKER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_gl_videomaker_reset (GstGLVideomaker* videomaker) +{ + if (videomaker->display) { + g_object_unref (videomaker->display); + videomaker->display = NULL; + } +} + + +static gboolean +gst_gl_videomaker_start (GstBaseTransform* bt) +{ + GstGLVideomaker* videomaker = GST_GL_VIDEOMAKER (bt); + + return TRUE; +} + +static gboolean +gst_gl_videomaker_stop (GstBaseTransform* bt) +{ + GstGLVideomaker* videomaker = GST_GL_VIDEOMAKER (bt); + + gst_gl_videomaker_reset (videomaker); + + return TRUE; +} + +static GstCaps * +gst_gl_videomaker_transform_caps (GstBaseTransform * bt, + GstPadDirection direction, GstCaps * caps) +{ + GstGLVideomaker* videomaker; + GstStructure* structure; + GstCaps *newcaps, *newothercaps; + GstStructure* newstruct; + const GValue* width_value; + const GValue* height_value; + const GValue* framerate_value; + const GValue* par_value; + + videomaker = GST_GL_VIDEOMAKER (bt); + + GST_ERROR ("transform caps %" GST_PTR_FORMAT, caps); + + structure = gst_caps_get_structure (caps, 0); + + width_value = gst_structure_get_value (structure, "width"); + height_value = gst_structure_get_value (structure, "height"); + framerate_value = gst_structure_get_value (structure, "framerate"); + par_value = gst_structure_get_value (structure, "pixel-aspect-ratio"); + + if (direction == GST_PAD_SINK) + { + newothercaps = gst_caps_new_simple ("video/x-raw-rgb", NULL); + newstruct = gst_caps_get_structure (newothercaps, 0); + gst_structure_set_value (newstruct, "width", width_value); + gst_structure_set_value (newstruct, "height", height_value); + gst_structure_set_value (newstruct, "framerate", framerate_value); + if (par_value) + gst_structure_set_value (newstruct, "pixel-aspect-ratio", par_value); + else + gst_structure_set (newstruct, "pixel-aspect-ratio", GST_TYPE_FRACTION, + 1, 1, NULL); + newcaps = gst_caps_new_simple ("video/x-raw-yuv", NULL); + gst_caps_append(newcaps, newothercaps); + } + else newcaps = gst_caps_new_simple ("video/x-raw-gl", NULL); + + newstruct = gst_caps_get_structure (newcaps, 0); + gst_structure_set_value (newstruct, "width", width_value); + gst_structure_set_value (newstruct, "height", height_value); + gst_structure_set_value (newstruct, "framerate", framerate_value); + if (par_value) + gst_structure_set_value (newstruct, "pixel-aspect-ratio", par_value); + else + gst_structure_set (newstruct, "pixel-aspect-ratio", GST_TYPE_FRACTION, + 1, 1, NULL); + + GST_ERROR ("new caps %" GST_PTR_FORMAT, newcaps); + + return newcaps; +} + +static gboolean +gst_gl_videomaker_set_caps (GstBaseTransform* bt, GstCaps* incaps, + GstCaps * outcaps) +{ + GstGLVideomaker* videomaker; + gboolean ret; + + videomaker = GST_GL_VIDEOMAKER (bt); + + GST_DEBUG ("called with %" GST_PTR_FORMAT, incaps); + + ret = gst_video_format_parse_caps (outcaps, &videomaker->video_format, + &videomaker->width, &videomaker->height); + + if (!ret) { + GST_ERROR ("bad caps"); + return FALSE; + } + + return ret; +} + +static gboolean +gst_gl_videomaker_get_unit_size (GstBaseTransform* trans, GstCaps* caps, + guint* size) +{ + gboolean ret; + GstStructure *structure; + gint width; + gint height; + + structure = gst_caps_get_structure (caps, 0); + if (gst_structure_has_name (structure, "video/x-raw-gl")) + { + GstVideoFormat video_format; + + ret = gst_gl_buffer_format_parse_caps (caps, &video_format, &width, &height); + if (ret) + *size = gst_gl_buffer_format_get_size (video_format, width, height); + } + else + { + GstVideoFormat video_format; + ret = gst_video_format_parse_caps (caps, &video_format, &width, &height); + if (ret) + *size = gst_video_format_get_size (video_format, width, height); + } + + return ret; +} + +static GstFlowReturn +gst_gl_videomaker_transform (GstBaseTransform* trans, GstBuffer* inbuf, + GstBuffer* outbuf) +{ + GstGLVideomaker* videomaker = NULL; + GstGLBuffer *gl_inbuf = GST_GL_BUFFER (inbuf); + + videomaker = GST_GL_VIDEOMAKER (trans); + + if (videomaker->display == NULL) + videomaker->display = g_object_ref (gl_inbuf->display); + else + g_assert (videomaker->display == gl_inbuf->display); + + GST_DEBUG ("making video %p size %d", + GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf)); + + //blocking call + gst_gl_display_videoChanged(videomaker->display, + videomaker->video_format, GST_BUFFER_DATA (outbuf)); + + return GST_FLOW_OK; +} diff --git a/gst/gl/gstglvideomaker.h b/gst/gl/gstglvideomaker.h new file mode 100644 index 0000000000..1b0ec7d506 --- /dev/null +++ b/gst/gl/gstglvideomaker.h @@ -0,0 +1,47 @@ +#ifndef _GST_GLVIDEOMAKER_H_ +#define _GST_GLVIDEOMAKER_H_ + +#include +#include +#include + +#include "gstglbuffer.h" + +G_BEGIN_DECLS + +#define GST_TYPE_GL_VIDEOMAKER (gst_gl_videomaker_get_type()) +#define GST_GL_VIDEOMAKER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_VIDEOMAKER,GstGLVideomaker)) +#define GST_IS_GL_VIDEOMAKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_VIDEOMAKER)) +#define GST_GL_VIDEOMAKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_VIDEOMAKER,GstGLVideomakerClass)) +#define GST_IS_GL_VIDEOMAKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_VIDEOMAKER)) +#define GST_GL_VIDEOMAKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_VIDEOMAKER,GstGLVideomakerClass)) + +typedef struct _GstGLVideomaker GstGLVideomaker; +typedef struct _GstGLVideomakerClass GstGLVideomakerClass; + +//typedef void (*GstGLVideomakerProcessFunc) (GstGLVideomaker *, guint8 *, guint); + +struct _GstGLVideomaker +{ + GstBaseTransform base_transform; + + GstGLDisplay *display; + GstVideoFormat video_format; + gint width; + gint height; +}; + +struct _GstGLVideomakerClass +{ + GstBaseTransformClass base_transform_class; +}; + +typedef struct _GstGLVideomaker GstGLVideomaker; +typedef struct _GstGLVideomakerClass GstGLVideomakerClass; + + +GType gst_gl_videomaker_get_type (void); + +G_END_DECLS + +#endif /* _GST_GLVIDEOMAKER_H_ */