The Good Giant
The Good Giant

Reputation: 1780

Gstreamer - appsrc push model

I'm developing a C application (under linux) that receives a raw h.264 stream and should visualize this stream using gstreamer APIs. I am a newbie with GStreamer so maybe I am doing huge stupid mistakes or ignoring well-known stuff, sorry about that.

I got a raw h264 video (I knew it was the exact same format I need) and developed an application that plays it. It correctly works with appsrc in pull mode (when need data is called, I get new data from the file and perform push-buffer).

Now I'm trying to do the exact same thing but in push mode, this is basically because I don't have a video, but a stream. So I have a method inside my code that will be called every time new data (in the form of an uint8_t buffer) arrives, and this is my video source.

I googled my problem and had a look at the documentation, but I found no simple code snippets for my use case, even if it seems a very simple one. I understood that I have to init the pipeline and appsrc and then only push-buffer when I have new data.

Well, I developed two methods: init_stream() for pipeline/appsrc initialization and populate_app(void *inBuf, size_t len) to send data when they are available. It compiles and correctly runs, but no video:

struct _App
{
    GstAppSrc *appsrc;
    GstPipeline *pipeline;
    GstElement *h264parse;
    GstElement *mfw_vpudecoder;
    GstElement *mfw_v4lsin;
    GMainLoop *loop;
};

typedef struct _App App;
App s_app;
App *app = &s_app;


static gboolean bus_message (GstBus * bus, GstMessage * message, App * app)
{
    GST_DEBUG ("got message %s", gst_message_type_get_name (GST_MESSAGE_TYPE (message)));

    switch (GST_MESSAGE_TYPE (message)) {
        case GST_MESSAGE_ERROR:
            g_error ("received error");
            g_main_loop_quit (app->loop);
            break;
        case GST_MESSAGE_EOS:
            g_main_loop_quit (app->loop);
            break;
        default:
            break;
    }
    return TRUE;
}

int init_stream()
{
    GstBus *bus;

    gst_init (NULL, NULL);
    fprintf(stderr, "gst_init done\n");

    /* create a mainloop to get messages */
    app->loop = g_main_loop_new (NULL, TRUE);
    fprintf(stderr, "app loop initialized\n");


    app->pipeline = gst_parse_launch("appsrc name=mysource ! h264parse ! mfw_vpudecoder ! mfw_v4lsin", NULL);

    app->appsrc = gst_bin_get_by_name (GST_BIN(app->pipeline), "mysource");
    gst_app_src_set_stream_type(app->appsrc, GST_APP_STREAM_TYPE_STREAM);

    gst_app_src_set_emit_signals(app->appsrc, TRUE);
    fprintf(stderr, "Pipeline and appsrc initialized\n");

    /* Create Bus from pipeline */
    bus = gst_pipeline_get_bus(app->pipeline);
    fprintf(stderr, "bus created\n");

    /* add watch for messages */
    gst_bus_add_watch (bus, (GstBusFunc) bus_message, app);
    gst_object_unref(bus);
    fprintf(stderr, "bus_add_watch done\n");

    GstCaps* video_caps = gst_caps_new_simple ("video/x-h264",
                                         "width", G_TYPE_INT, 800,
                                         "height", G_TYPE_INT, 480,
                                         "framerate", GST_TYPE_FRACTION, 25,
                                         1, NULL);

    gst_app_src_set_caps(GST_APP_SRC(app->appsrc), video_caps);

    /* go to playing and wait in a mainloop. */
    gst_element_set_state ((GstElement*) app->pipeline, GST_STATE_PLAYING);
    fprintf(stderr, "gst_element_set_state play\n");

    /* this mainloop is stopped when we receive an error or EOS */
    g_main_loop_run (app->loop);
    fprintf(stderr, "g_main_loop_run called\n");

    gst_element_set_state ((GstElement*) app->pipeline, GST_STATE_NULL);

    fprintf(stderr, "gst_element_set_state GST_STATE_NULL\n");

    /* free the file */
    // g_mapped_file_unref (app->file);

    gst_object_unref (bus);
    g_main_loop_unref (app->loop);

    return 0;
}

void populateApp(void  *inBuf ,  size_t len) {

    guint8 *_buffer = (guint8*) inBuf;
    GstFlowReturn ret;
    GstBuffer *buffer = gst_buffer_new();

    GstCaps* video_caps = gst_caps_new_simple ("video/x-h264",
                                               "width", G_TYPE_INT, 800,
                                               "height", G_TYPE_INT, 480,
                                               "framerate", GST_TYPE_FRACTION, 25,
                                               1, NULL);

gst_buffer_set_caps(buffer, video_caps);

    GST_BUFFER_DATA (buffer) = _buffer;
    GST_BUFFER_SIZE (buffer) = len;

    // g_signal_emit_by_name (app->appsrc, "push-buffer", buffer, &ret);
    ret = gst_app_src_push_buffer(GST_APP_SRC(app->appsrc), buffer);
    gst_buffer_unref (buffer);
}

As said, I am a total newbie at GStreamer so there's a lot of cut-and-paste code from the internet, but IMHO it should work. Do you see any issues?

Upvotes: 3

Views: 4613

Answers (1)

jhauris
jhauris

Reputation: 1143

It's not clear how you are calling populateApp, but you need to call that repeatedly as you have data to push to your pipeline. This can be done in a separate thread from the one blocked by g_main_loop_run, or you can restructure your program to avoid using GMainLoop.

Upvotes: 1

Related Questions