Search code examples
clinuxaudiogstreamer

How to play audio with gstreamer in C?


I'm trying to play audio with Gstreamer in C. I'm using Laptop, Ubuntu 16.

To play I use this pipeline and it's working:

gst-launch-1.0 filesrc location=lambo-engine.mp3 ! decodebin ! audioconvert ! autoaudiosink

But when I convert it to C:

GstElement *pipeline, *source, *decode, *audioconvert, *sink;
GMainLoop *loop;
GstBus *bus;
GstMessage *msg;

int main(int argc, char *argv[]) {
    /* Initialize GStreamer */
    gst_init (&argc, &argv);

    /* Create the elements */
    source          = gst_element_factory_make("filesrc",  NULL);
    decode          = gst_element_factory_make("decodebin",     NULL);
    audioconvert    = gst_element_factory_make("audioconvert",  NULL);
    sink            = gst_element_factory_make("autoaudiosink", NULL);
    // Set parameters for some elements
    g_object_set(G_OBJECT(source),  "location", "lambo-engine.mp3", NULL);
    /* Create the empty pipeline */
    pipeline = gst_pipeline_new ("pipeline");

    /* Build the pipeline */
    gst_bin_add_many (GST_BIN (pipeline), source, decode, audioconvert, sink, NULL);

      if (gst_element_link_many(source, decode, audioconvert, sink, NULL) != TRUE){
        g_error("Failed to link save elements!");
        gst_object_unref (pipeline);
        return -1;
    }
  
    /* Start playing */
    gst_element_set_state (pipeline, GST_STATE_PLAYING);

    bus = gst_element_get_bus (pipeline);
    gst_object_unref (bus);

    /* now run */
    g_main_loop_run (loop);

    /* Free pipeline */
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (GST_OBJECT(pipeline));
    return 0;
}

I can built it success. But when I run it, it return error can't link elements:

** (example:2516): ERROR **: 22:59:42.310: Failed to link save elements! Trace/breakpoint trap (core dumped)

Someone helps me to figure out error, please.

Thank you so much


Solution

  • Gstreamer is centered around pipelines that are lists of elements. Elements have pads to exchange data on. In your example, decodebin has an output pad and audioconvert has an input pad. At the start of the pipeline, the pads need to be linked.

    This is when pads agree on the format of data, as well as some other information, such as who's in charge of timing and maybe some more format specifics.

    Your problem arises from the fact that decodebin is not actually a real element. At runtime, when filesrc starts up, it tells decodebin what pad it has, and decodebin internally creates elements to handle that file.

    For example: filesrc location=test.mp4 ! decodebin would run in this order:

    • delay linking because types are unknown
    • start filesrc
    • filesrc says "trying to link, I have a pad of format MP4(h264)
    • decodebin sees this request, and in turn, creates on the fly a h264 parse element that can handle the mp4 file
    • decodebin now has enough information to describe it's pads, and it links the rest of the pipeline
    • video starts playing

    Because you are using c to do this, you link the pipeline before filesrc loads the file. This means that decodebin doesn't know the format of it's pads at startup, and therefore fails to link.

    To fix this, you have two options:

    1.) Swap out decodebin for something that supports only one type. If you know your videos will always be mp4s with h264, for example, you can use h264parse instead of decodebin. Because h264parse only works with one type of format, it knows it's pad formats at the start, and will be able to link without issue.

    2.) Reimplement the smart delaying linking. You can read the docs for more info, but you can delay linking of the pipeline, and install callbacks to complete the linking when there's enough information. This is what gst-launch-1.0 is doing under the hood. This has the benefit of being more flexible: anything support by decodebin will work. The downside is that it's much more complex, involves a nontrivial amount of work on your end, and is more fragile. If you can get away with it, try fix 1