Skip to content

Instantly share code, notes, and snippets.

@sphaero
Last active June 24, 2019 18:10
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sphaero/eac4d01013bd2b0e2205 to your computer and use it in GitHub Desktop.
Save sphaero/eac4d01013bd2b0e2205 to your computer and use it in GitHub Desktop.
Gstreamer glimagesink raspberry pi generic example
/*
* GStreamer GLES2 Raspberry Pi example
* Based on http://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/tests/examples/gl/generic/cube/main.cpp
* Modified for Raspberry Pi/GLES2 by Arnaud Loonstra <arnaud@sphaero.org>
* Orginal by Julien Isorce <julien.isorce@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* Compile on a RPI with latest Gstreamer in /usr/local
$ g++ glesgeneric.c -pthread -I/usr/include/gstreamer-1.0 \
-I/usr/include/glib-2.0 \
-I/usr/lib/arm-linux-gnueabihf/glib-2.0/include \
-I/usr/include/libdrm \
-I/opt/vc/include/ \
-I/usr/local/include/gstreamer-1.0/ \
-L/opt/vc/lib/ -lgstreamer-1.0 -lgobject-2.0 \
-lglib-2.0 -lGLESv2 -lEGL
*/
#include <GLES/gl.h>
#include <GLES2/gl2.h>
#include <gst/gst.h>
#include <stdlib.h>
#include <iostream>
#include <string>
static const gchar *simple_vertex_shader_str_gles2 =
"attribute vec4 a_position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";
static const gchar *simple_fragment_shader_str_gles2 =
"#ifdef GL_ES \n"
"precision mediump float; \n"
"#endif \n"
"varying vec2 v_texCoord; \n"
"uniform sampler2D tex; \n"
"void main() \n"
"{ \n"
" gl_FragColor = texture2D( tex, v_texCoord ); \n"
"} \n";
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
///
// Create a shader object, load the shader source, and
// compile the shader.
//
GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader ( type );
if ( shader == 0 )
return 0;
// Load the shader source
glShaderSource ( shader, 1, &shaderSrc, NULL );
// Compile the shader
glCompileShader ( shader );
// Check the compile status
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char* infoLog = (char*)malloc (sizeof(char) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
std::cout << "Error compiling shader:\n" << infoLog << "\n";
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;
}
GLint initGL() {
// load vertext/fragment shader
vertexShader = LoadShader ( GL_VERTEX_SHADER, simple_vertex_shader_str_gles2 );
fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, simple_fragment_shader_str_gles2 );
// Create the program object
programObject = glCreateProgram();
if ( programObject == 0 )
{
std::cout << "error program object\n";
return 0;
}
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
// Bind vPosition to attribute 0
glBindAttribLocation(programObject, 0, "a_position");
// Link the program
glLinkProgram(programObject);
// Check the link status
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked )
{
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char* infoLog = (char*)malloc (sizeof(char) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
std::cout << "Error linking program:\n" << infoLog << "\n";
free ( infoLog );
}
glDeleteProgram ( programObject );
return GL_FALSE;
}
return GL_TRUE;
}
static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{
GMainLoop *loop = (GMainLoop*)data;
switch (GST_MESSAGE_TYPE (msg))
{
case GST_MESSAGE_EOS:
g_print ("End-of-stream\n");
g_main_loop_quit (loop);
break;
case GST_MESSAGE_ERROR:
{
gchar *debug = NULL;
GError *err = NULL;
gst_message_parse_error (msg, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
if (debug)
{
g_print ("Debug deails: %s\n", debug);
g_free (debug);
}
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
//client reshape callback
static gboolean reshapeCallback (void *gl_sink, void *gl_ctx, GLuint width, GLuint height, gpointer data)
{
//std::cout << "Reshape: width=" << width << " height=" << height << "\n";
glViewport(0, 0, width, height);
return TRUE;
}
//client draw callback
static gboolean drawCallback (void * gl_sink, void * gl_ctx, GLuint texture, GLuint width, GLuint height, gpointer data)
{
//std::cout << "draw:" << vertexShader << ":" << fragmentShader << ":" << programObject << ":" << linked << "\n";
if (!linked) {
initGL();
}
GLfloat vVertices[] = { -0.5f, 0.5f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-0.5f, -0.5f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
0.5f, -0.5f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
0.5f, 0.5f, 0.0f, // Position 3, skewed a bit
1.0f, 0.0f // TexCoord 3
};
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
glClear ( GL_COLOR_BUFFER_BIT );
glUseProgram ( programObject );
// Load the vertex position
GLint positionLoc = glGetAttribLocation ( programObject, "a_position" );
glVertexAttribPointer ( positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vVertices );
// Load the texture coordinate
GLint texCoordLoc = glGetAttribLocation ( programObject, "a_texCoord");
glVertexAttribPointer ( texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]);
glEnableVertexAttribArray ( positionLoc );
glEnableVertexAttribArray ( texCoordLoc );
glActiveTexture ( GL_TEXTURE0 );
glBindTexture (GL_TEXTURE_2D, texture);
//Set the texture sampler to texture unit 0
GLint tex = glGetUniformLocation ( programObject, "tex");
glUniform1i ( tex, 0 );
glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );
}
//gst-launch-1.0 videotestsrc num_buffers=400 ! video/x-raw, width=320, height=240 !
//glgraphicmaker ! glfiltercube ! video/x-raw, width=800, height=600 ! glimagesink
gint main (gint argc, gchar *argv[])
{
GstStateChangeReturn ret;
GstElement *pipeline, *videosrc, *glimagesink;
GMainLoop *loop;
GstBus *bus;
/* initialization */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* create elements */
pipeline = gst_pipeline_new ("pipeline");
/* watch for messages on the pipeline's bus (note that this will only
* work like this when a GLib main loop is running) */
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus);
/* create elements */
videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc0");
glimagesink = gst_element_factory_make ("glimagesink", "glimagesink0");
if (!videosrc || !glimagesink)
{
g_print ("one element could not be found \n");
return -1;
}
/* change video source caps */
GstCaps *caps = gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "RGB",
"width", G_TYPE_INT, 320,
"height", G_TYPE_INT, 240,
"framerate", GST_TYPE_FRACTION, 25, 1,
NULL) ;
/* configure elements */
g_object_set(G_OBJECT(videosrc), "num-buffers", 400, NULL);
g_signal_connect(G_OBJECT(glimagesink), "client-reshape", G_CALLBACK (reshapeCallback), NULL);
g_signal_connect(G_OBJECT(glimagesink), "client-draw", G_CALLBACK (drawCallback), NULL);
/* add elements */
gst_bin_add_many (GST_BIN (pipeline), videosrc, glimagesink, NULL);
/* link elements */
gboolean link_ok = gst_element_link_filtered(videosrc, glimagesink, caps) ;
gst_caps_unref(caps) ;
if(!link_ok)
{
g_warning("Failed to link videosrc to glimagesink!\n") ;
return -1 ;
}
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE)
{
g_print ("Failed to start up pipeline!\n");
/* check if there is an error message with details on the bus */
GstMessage* msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
if (msg)
{
GError *err = NULL;
gst_message_parse_error (msg, &err, NULL);
g_print ("ERROR: %s\n", err->message);
g_error_free (err);
gst_message_unref (msg);
}
return -1;
}
// run loop
g_main_loop_run (loop);
/* clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return 0;
}
@stuaxo
Copy link

stuaxo commented Nov 1, 2016

I had a go at using this with gst-uninstalled on raspbian.

Build command was

g++ glesgeneric.c $(~/gst/gst-master pkg-config --cflags --libs glib-2.0 gobct-2.0 gstreamer-1.0 glesv2 egl)

But screen stayed black.

Uncommenting some of the debug statements I got a lot of

draw:10:11:12:1
draw:10:11:12:1
draw:10:11:12:1
draw:10:11:12:1
draw:10:11:12:1
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment