SFML community forums

General => General discussions => Topic started by: Zapfyr on October 09, 2012, 03:23:02 pm

Title: GStreamer SDK and SFML
Post by: Zapfyr on October 09, 2012, 03:23:02 pm
Hi,

Using  GStreamer SDK (http://gstreamer.com/ (http://gstreamer.com/), i.e. GStreamer that is cross-platform) together with SFML would be cool. I.e. it would be pretty awesome to use it to decode a movie to a texture and use it in a sprite in SFML. As I am pretty new to both SFML and GStreamer SDK it would be awesome if someone with a bit more experience could give me hint or two how I could achieve the whole decode a movie to a texture and render it trough a sprite-process. :)

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Laurent on October 09, 2012, 03:30:22 pm
The sfeMovie (http://en.sfml-dev.org/forums/index.php?topic=8701.0) project does what you want. You can use it directly if using ffmpeg instead of GStreamer is not a problem for you, or you can look at its sources if you want to implement your own movie library.
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on October 09, 2012, 03:37:45 pm
Sorry, I should have mentioned that I have tried sfeMovie, but I found it quite heavy (performance-wise), and if I am not mistaken; lacking the ability to seek in a movie. Using gstreamer the possibillity for hardware acceleration (for example using the vaapi plugin) opens up as well. But I will take a second look into the sfeMovie project, thanks.

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Laurent on October 09, 2012, 03:40:36 pm
Yes, but its sources can answer your question
Quote
how I could achieve the whole decode a movie to a texture and render it trough a sprite-process.
Title: Re: GStreamer SDK and SFML
Post by: eXpl0it3r on October 09, 2012, 04:47:56 pm
Someone once wrote a GStreamer wrapper for/with SFML, but that post was removed on the forum and I don't remember the GitHub adress...
Maybe some Googling will bring it to the light. ;)
Title: Re: GStreamer SDK and SFML
Post by: eXpl0it3r on October 19, 2012, 01:17:17 pm
If you're still interested in that wrapper I mentioned, I've just noticed that I stared the project on GitHub so I was able to find it again. ;D
But it seems like he switched from SFML to SDL, so you might need to rewrite some parts or checkout a older commit and apply the changes manually...

_2RealGStreamerWrapper (https://github.com/cadet/_2RealGStreamerWrapper/)
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on October 19, 2012, 01:23:12 pm
Thanks, man! :)

I, however, started building my own, based on OSSBuild, as GStreamer SDK doesn't include the OpenGL plugin (which OSSBuild does).

I will report back how it goes, later. ;)

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Ceylo on October 19, 2012, 07:56:20 pm
Sorry, I should have mentioned that I have tried sfeMovie, but I found it quite heavy (performance-wise), and if I am not mistaken; lacking the ability to seek in a movie. Using gstreamer the possibillity for hardware acceleration (for example using the vaapi plugin) opens up as well. But I will take a second look into the sfeMovie project, thanks.

// Zap
You're right about CPU usage, sfeMovie is heavy although it's usable. You're also right about the missing seeking feature.

However I don't think making your own player from scratch is the best idea. Here is why:
- sfeMovie is already *a lot* of work. I've been working on it for two years now, so unless you're targeting very specific software/hardware or unless you're already much experienced with video stuff/really smart, I believe you would get things done in less time if you rather fixed what's missing in sfeMovie (as your own project or by participating to sfeMovie, that's up to you)
- concerning hardware accelerators, FFmpeg (on which sfeMovie relies) provides these ones:
Code: [Select]
h263_vaapi mpeg2_dxva2 vc1_vaapi
h264_dxva2 mpeg2_vaapi wmv3_dxva2
h264_vaapi mpeg2_vdpau wmv3_vaapi
h264_vda mpeg4_vaapi
mpeg1_vdpau vc1_dxva2
I guess these would fit your needs. In order to be enabled, FFmpeg must be built on a host that support these accelerators, that way the FFmpeg configure script will be able to detect and enable support for these accelerators.
- as for seeking, I had been working on it but never included the changes as it isn't finished yet. You can look at what had been done on the seeking branch (https://github.com/Yalir/sfeMovie/tree/seeking).

If you still want to make your own player, here are some points you should have a close look at:
- audio/video syncing
- audio/video decoding latency handling
- video stream structure: especially the fact that most frames in a video file are incomplete frames, which prevents you from simply seeking to a specific time

In any case, good luck :)
Ceylo
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on October 19, 2012, 08:46:47 pm
The sfeMovie project seems awesome, and I do not doubt that I could use it. However trying out the tutorials for GStreamer SDK I found it good performance-wise as well as quite easy to use and install, for being a very flexible, large and complex SDK. Which that is why I will try to use it in my project. However if that fails miserably I will probably try to use sfeMovie instead.

My (initial) plan is to use GStreamers playbin2 and the gst-plugin-gl (OpenGL plugin for GStreamer, which is included in the OSSBuild) to draw video as a sprite (or at least on a textured quad). As the plugin will give you an actual OpenGL-texture-id when a frame has been decoded. The plugin enables the current frame to be uploaded to the GPU without any work, except for the actual setup of the pipeline . Decoding a 1080p h264 movie with the help of playbin2 works very nice, getting a very similar preformenc as VLC and similar media-players on Windows (haven't tried it on *nix just yet).

Quote
If you still want to make your own player, here are some points you should have a close look at:
- audio/video syncing
- audio/video decoding latency handling
- video stream structure: especially the fact that most frames in a video file are incomplete frames, which prevents you from simply seeking to a specific time
As far as I know GStreamer does all this for you.

Some interesting links:
GStreamer SDK tutorials: http://docs.gstreamer.com/display/GstSDK/Tutorials (http://docs.gstreamer.com/display/GstSDK/Tutorials)
OSSBuild, GStreamer that includes the OpenGL plugin and a lot more. http://code.google.com/p/ossbuild/ (http://code.google.com/p/ossbuild/)

GStreamer SDK and the OSSBuild works nicely on Windows in VS2010.

Here is a very simple example (from gst-plugin-gl) of how to use the OpenGL plugin, drawing a test video on the sides of a cube:
#include <GL/glew.h>
#include <gst/gst.h>

#include <iostream>
#include <string>

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
void reshapeCallback (GLuint width, GLuint height, gpointer data)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100);
    glMatrixMode(GL_MODELVIEW);
}

//client draw callback
gboolean drawCallback (GLuint texture, GLuint width, GLuint height, gpointer data)
{
    static GLfloat      xrot = 0;
    static GLfloat      yrot = 0;
    static GLfloat      zrot = 0;
    static GTimeVal current_time;
    static glong last_sec = current_time.tv_sec;
    static gint nbFrames = 0;

    g_get_current_time (&current_time);
    nbFrames++ ;

    if ((current_time.tv_sec - last_sec) >= 1)
    {
        std::cout << "GRPHIC FPS = " << nbFrames << std::endl;
        nbFrames = 0;
        last_sec = current_time.tv_sec;
    }

    glEnable(GL_DEPTH_TEST);

    glEnable (GL_TEXTURE_RECTANGLE_ARB);
    glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture);
    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);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(0.0f,0.0f,-5.0f);

    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);
    glRotatef(zrot,0.0f,0.0f,1.0f);

    glBegin(GL_QUADS);
              // Front Face
              glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
              glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f,  1.0f,  1.0f);
              glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f,  1.0f,  1.0f);
              // Back Face
              glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
              glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f,  1.0f, -1.0f);
              glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f,  1.0f, -1.0f);
              glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
              // Top Face
              glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f,  1.0f, -1.0f);
              glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
              glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f,  1.0f, -1.0f);
              // Bottom Face
              glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
              glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f,  1.0f);
              glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f,  1.0f);
              // Right face
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
              glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f,  1.0f, -1.0f);
              glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f,  1.0f,  1.0f);
              glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
              // Left Face
              glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
              glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
              glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f,  1.0f,  1.0f);
              glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f,  1.0f, -1.0f);
    glEnd();

    xrot+=0.3f;
    yrot+=0.2f;
    zrot+=0.4f;

    //return TRUE causes a postRedisplay
    return TRUE;
}


//gst-launch-0.10 videotestsrc num_buffers=400 ! video/x-raw-rgb, width=320, height=240 !
//glgraphicmaker ! glfiltercube ! video/x-raw-gl, width=800, height=600 ! glimagesink
gint main (gint argc, gchar *argv[])
{
    GstStateChangeReturn ret;
    GstElement *pipeline, *videosrc, *glupload, *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");
    glupload  = gst_element_factory_make ("glupload", "glupload0");
    glimagesink  = gst_element_factory_make ("glimagesink", "glimagesink0");


    if (!videosrc || !glupload || !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-rgb",
                                        "width", G_TYPE_INT, 320,
                                        "height", G_TYPE_INT, 240,
                                        "framerate", GST_TYPE_FRACTION, 25, 1,
                                        NULL) ;


    GstCaps *outcaps = gst_caps_new_simple("video/x-raw-gl",
                                        "width", G_TYPE_INT, 800,
                                        "height", G_TYPE_INT, 600,
                                        NULL) ;

    /* configure elements */
    g_object_set(G_OBJECT(videosrc), "num-buffers", 400, NULL);
    g_object_set(G_OBJECT(glimagesink), "client-reshape-callback", reshapeCallback, NULL);
    g_object_set(G_OBJECT(glimagesink), "client-draw-callback", drawCallback, NULL);
    g_object_set(G_OBJECT(glimagesink), "client-data", NULL, NULL);

    /* add elements */
    gst_bin_add_many (GST_BIN (pipeline), videosrc, glupload, glimagesink, NULL);

    /* link elements */
    gboolean link_ok = gst_element_link_filtered(videosrc, glupload, caps) ;
    gst_caps_unref(caps) ;
    if(!link_ok)
    {
        g_warning("Failed to link videosrc to glupload!\n") ;
        return -1 ;
    }
    link_ok = gst_element_link_filtered(glupload, glimagesink, outcaps) ;
    gst_caps_unref(outcaps) ;
    if(!link_ok)
    {
        g_warning("Failed to link glupload to glimagesink!\n") ;
        return -1 ;
    }


    /* run */
    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;
    }

    g_main_loop_run (loop);

    /* clean up */
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (pipeline);

    return 0;

}
 

I do not yet know exacly how I will setup the actual rendering of the quad in SFML, I might try to sub-class the Drawable(?) class or draw the thing in "pure" OpenGL.

The goal is to be able to draw at-least 3 (hopefully more) 1080p movies at the same time. :P

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Ceylo on October 19, 2012, 09:43:49 pm
Looks really interesting! I had seen GStreamer but I didn't do any deep searches as FFmpeg fitted my needs.

I would really like to read your comments about GStreamer once you've got it to work with SFML.
From what I saw it's indeed much better documented. I do not know what about the ease of use. It does seem to support decoding/playing but not encoding though (which is quite useless for sfeMovie, I agree).
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on October 26, 2012, 10:00:12 am
Hi,

Just reporting on how the project is working out. I have given up on using the OpenGL plugin for GStreamer. However I have successfully drawn a full HD movie to a texture using an other pipeline (a modified playbin2). Using PBOs (2 actually) and preforming a kinda buffer ping-ponging to make use of the DMA (async coping etc. that does not block the CPU) I have managed to get pretty good performance. What remains now is to properly subclass SFMLs Drawable and Transformable so that my "VideoSprite" works similar to SFMLs sprite.

This is my system info (graphics: AMD HD7870 Eyefinity Edition):
(http://tux.servegame.org/~zapfyr/temp/systemInfo.png)

When running two 1080p movies at the same time on a windows that is 3840x1080:
(http://tux.servegame.org/~zapfyr/temp/sfml_video_gstreamer.png)

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on November 12, 2012, 09:22:44 am
Hi (again).

Since I last wrote much testing using GStreamer have been done. It seems to be working great .... on Linux ....  on Windows it SEEMS to be not that mature. I have run into trouble when trying to run a looped movie for a long time, it loops for maybe half a day (4 - 12 hours) and then something inside GStreamer breaks and I get a general streaming error. I have tested it both with the GStreamer SDK and OSSBuild and with multiple movies coded in a number of different ways. Now this may of course be my fault, but as I can't find much when googling and can't see anything wrong with my code I will now look for other solutions.

As for GStreamer SDK vs. OSSBuild, in many cases GStreamer SDK gives at lot less choppy playback and seems to get better performance (not that surprising as the SDK uses someting like 0.10.36 and OSSBuild 0.10.28 of GStreamer).

GStreamer SDK using GStreamer 1.0 is about to be release some time this month (Nov. 2012) which may make it more stable for Windows.

As of now I will be looking into sfeMovie instead. :)

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Ceylo on November 13, 2012, 09:35:25 pm
Did you ask GStreamer's people for your looping issue?
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on November 14, 2012, 10:57:53 am
I tried posting about it on one of their mailing-lists. Haven't got a response from anyone yet tho. As far as I know there are no "normal" forums or similar that I can turn to, which kinda makes it hard to find any good answers to what is happening inside gstreamer. :(

I haven't asked about it on sites like stackoverflow, yet.

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Ceylo on November 14, 2012, 06:03:58 pm
Your question on the mailing list is just too big. I don't expect anyone to answer. And I don't think you'll get better results on stackoverflow with such a big message. Write a much shorter one if you want answers.

Now if you wish to help at improving sfeMovie, which feature is the most important to you?
- performance: I don't think much can be done except hardware acceleration if available
- seeking: some seeking algorithms can be done rather easily (see here (http://en.sfml-dev.org/forums/index.php?topic=3463.msg51206#msg51206)) but the most interesting one requires much more work
- looping: non-smooth looping can also be done easily, but good looping also needs much more work (most probably a complete redesign to do pipelined and buffered decoding)

I would of course also help in case you're interested in this :)
Ceylo
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on November 14, 2012, 06:59:02 pm
Yeah, I do not expect an answer either, not only because my question is large (and vague) but because the few similar questions I bumped into when googling about the error didn't have any solutions. Debugging a general streaming error is not easy. :) I even tried to get it to work by destroying the pipeline and clean up everything when I got the EOS message and rebuild it again afterwards, this resulted in very similar (if not exact) results as seeking to the beginning. It seems to be some kind of memory problem, e.g. that I do not free something and that gstreamer runs out of some kind of internal buffer, but that is just speculation and as the actual setup of the pipeline as well as the handling (and seeking) are all in the GStreamer SDK tutorials my code should be correct ...

About sfeMovie; I have built it on Windows with everything that ffmpeg gives us. I got it to loop (not perfect but good enough, by stopping and playing when the movie reaches its end) and run it for about 26 hours (before I turned it off), which is great. :) I would love to help out with the project, I just need to find the time. Performance is probably the most important thing right now and after that looping, I am not that interested in seeking. When I did the texture upload code from GStreamer to OpenGL I got a pretty nice performance boost when I switched from using sfml textures, using the update function, to using PBO ping ponging which makes it possible to stream texture data asynchronously without blocking the CPU. Is that something that may help the sfeMovie project or do you want me to look into something else?

// Zap
Title: Re: GStreamer SDK and SFML
Post by: Ceylo on November 14, 2012, 10:00:39 pm
Hmmm ok. I'm pleased to know sfeMovie is now stable enough to run for 26 hours without crashing ;D .

The first thing to do would be to find out what is consuming most CPU time. From what I remember it's decoding, not the texture upload. We would also need to make a difference between blocking operations and CPU time consuming operations. I don't know whether we can easily make the difference.

And then if the texture update is indeed costly, yes why not give it a try with PBO. But note that I don't want to make a library that works only on specific hardware. If PBO can't be used everywhere, there *must* be a fallback method.
Title: Re: GStreamer SDK and SFML
Post by: Zapfyr on September 27, 2013, 02:14:30 pm
I know this is an old post but I felt that I should post about how it is going and share some code that may help others. I created a class that uses GStreamer to decode a movie and SFML to render it (trough some OpenGL). Note that the code wont work as it is now as I stopped working on it and left it a bit messy (sorry about that), it also need some libs (e.g. boost and GStreamer).

When it works it uses PBOs (Pixel Buffer Object) to copy pixel from RAM to VRAM, it also uses ping poing to speed up copying even more. I ran it on Windows 7 and Windows 8 using GStreamer SDKhttp://www.gstreamer.com/ (http://www.gstreamer.com/) and SFML 2.0.

http://tux.servegame.org/~zapfyr/VideoSprite.hpp (http://tux.servegame.org/~zapfyr/VideoSprite.hpp)
http://tux.servegame.org/~zapfyr/VideoSprite.cpp (http://tux.servegame.org/~zapfyr/VideoSprite.cpp)