#include <gst/gst.h>

static gboolean
my_bus_callback(
    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;
            GError* error;

            gst_message_parse_error(msg, &error, &debug);
            g_free(debug);

            g_printerr("Error: %s\n", error->message);
            g_error_free(error);

            g_main_loop_quit(loop);
            break;
        }

        default:
            break;
    }

    return TRUE;
}

GstElement *pipeline, *audio;

static void
cb_newpad(
    GstElement* uridecodebin,
    GstPad* pad,
    gpointer data
)
{
    GstCaps* caps;
    GstStructure* str;
    GstPad* audio_pad;

    /* only link once */
    audio_pad = gst_element_get_static_pad(audio, "sink");
    if (GST_PAD_IS_LINKED(audio_pad)) {
        g_object_unref(audio_pad);
        return;
    }

    /* check media type */
    caps = gst_pad_query_caps(pad, NULL);
    str = gst_caps_get_structure(caps, 0);
    if (!g_strrstr(gst_structure_get_name(str), "audio")) {
        gst_caps_unref(caps);
        gst_object_unref(audio_pad);
        return;
    }
    gst_caps_unref(caps);

    /* link'n'play */
    gst_pad_link(pad, audio_pad);

    g_object_unref(audio_pad);
}

gint
main(
    gint argc,
    gchar* argv[]
)
{
    GMainLoop* loop;
    GstElement *uridec, *conv, *sink;
    GstPad* audio_pad;
    GstBus* bus;

    /* init GStreamer */
    gst_init(&argc, &argv);
    loop = g_main_loop_new(NULL, FALSE);

    /* make sure we have input */
    if (argc != 2) {
        g_print("Usage: %s <uri>\n", argv[0]);
        return -1;
    }

    /* setup */
    pipeline = gst_pipeline_new("pipeline");

    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
    gst_bus_add_watch(bus, my_bus_callback, loop);
    gst_object_unref(bus);

    uridec = gst_element_factory_make("uridecodebin", "decoder");
    g_object_set(G_OBJECT(uridec), "uri", argv[1], NULL);
    g_signal_connect(uridec, "pad-added", G_CALLBACK(cb_newpad), NULL);
    gst_bin_add_many(GST_BIN(pipeline), uridec, NULL);

    /* create audio output */
    audio = gst_bin_new("audiobin");
    conv = gst_element_factory_make("audioconvert", "aconv");
    audio_pad = gst_element_get_static_pad(conv, "sink");
    sink = gst_element_factory_make("alsasink", "sink");
    gst_bin_add_many(GST_BIN(audio), conv, sink, NULL);
    gst_element_link(conv, sink);
    gst_element_add_pad(
        audio,
        gst_ghost_pad_new("sink", audio_pad)
    );
    gst_object_unref(audio_pad);
    gst_bin_add(GST_BIN(pipeline), audio);

    /* run */
    gst_element_set_state(pipeline, GST_STATE_PLAYING);
    g_main_loop_run(loop);

    /* cleanup */
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(pipeline));

    return 0;
}