
#include <gst/gst.h>
#include <unistd.h>
#include <stdio.h>


#define VDEPAY  "rtph264depay"
#define ADEPAY  "rtpmp4adepay"
#define VDEC    "avdec_h264"
#define ADEC    "faad"

static GMainLoop* loop;
static GstElement *pipeline, *source, *vdec, *vsink, *adec, *asink;
static GstElement *vdepay, *adepay;
static GstPadProbeReturn
    on_pad_probe(GstPad *pad, GstPadProbeInfo *info, gpointer data);

static gboolean
bus_call (GstBus *bus, GstMessage *msg, gpointer data){

    switch(GST_MESSAGE_TYPE(msg))
    {
        case GST_MESSAGE_EOS:
            g_print("EOS Received\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:
            //g_print("--->:%s\n", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
            break;
    }
    return TRUE;
}

static void
on_pad_added (GstElement *element, GstPad *pad, gpointer data)
{
    GstPad* sinkpad;
    GstCaps* caps = gst_pad_query_caps (pad, NULL);
    gchar* str = gst_caps_to_string (caps); 

    g_print("%s\n", str);

    if (g_strrstr (str, "audio"))
    {
        g_print ("connecting audio\n");
        sinkpad = gst_element_get_static_pad (adepay, "sink");
    }
    else if (g_strrstr (str, "video"))
    {
        g_print ("connecting video\n");
        sinkpad = gst_element_get_static_pad (vdepay, "sink");
    }
    else
    {
        g_print("Warning: Unexpected pad type\n");
    }

    if(sinkpad != NULL)
    {
        gst_pad_link (pad, sinkpad);
        g_object_unref (sinkpad);
    }

    g_free (str);
    gst_caps_unref (caps);
}


static quit = 0;

static gboolean
func (gpointer data)
{
    if (quit)
        g_main_loop_quit (loop);
    return TRUE;
}


static void 
intHandler (int signal) 
{
    quit = 1;
}



static gboolean 
infunc (GIOChannel* channel, GIOCondition condition, gpointer data)
{
    int fd , br;
    gchar buffer[256];

    fd = g_io_channel_unix_get_fd (channel);
    br = read(fd, buffer, 256);
    buffer[br-1] = '\0';
      
    if(!strcmp(buffer, "p")) {
        gst_element_set_state (pipeline, GST_STATE_PAUSED);
    }
    else if (!strcmp(buffer, "r")){
        gst_element_set_state (pipeline, GST_STATE_PLAYING);
    }

    return TRUE;  
}


int main (int argc, char *argv[])
{

    GstPad *pad;
    GstBus *bus;
    GIOChannel *in;

    /* init */
    gst_init (&argc, &argv);

    if(argc != 2){
        g_print("file path is required\n");
        return -1;
    }

    in = g_io_channel_unix_new (fileno(stdin));
    g_io_add_watch(in, G_IO_IN, infunc, NULL);

    /* create elements */
    pipeline = gst_pipeline_new ("my_pipeline");
    source = gst_element_factory_make("rtspsrc", "source");

    vdepay = gst_element_factory_make(VDEPAY, NULL);
    vdec = gst_element_factory_make(VDEC, NULL);
    vsink = gst_element_factory_make("xvimagesink", NULL);
    
    adepay = gst_element_factory_make(ADEPAY, NULL);
    adec = gst_element_factory_make(ADEC, NULL);
    asink = gst_element_factory_make("alsasink", NULL);

    if(!pipeline || !source || !vdec || !vdepay || !vsink ||
        !adepay || !adec || !asink){
        g_print("Error in creating element\n");
        return -1;
    }

    g_object_set(G_OBJECT(source), "location", argv[1], NULL);
    g_object_set(G_OBJECT(source), "buffer-mode", 2, NULL);
//    g_object_set(G_OBJECT(source), "ntp-sync", TRUE, NULL);

    gst_bin_add_many (GST_BIN (pipeline), source, vdepay, vdec, vsink,  adepay, adec, asink, NULL);
    gst_element_link_many (vdepay, vdec, vsink, NULL);
    gst_element_link_many (adepay, adec, asink, NULL);

    g_signal_connect(source, "pad-added", G_CALLBACK(on_pad_added), NULL);

    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
    gst_bus_add_watch (bus, bus_call, NULL);
    gst_object_unref (bus);

    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    signal(SIGINT, intHandler);

    loop = g_main_loop_new(NULL,FALSE);
    g_idle_add (func, NULL);
    g_main_loop_run (loop);

    g_print ("############################# QUITTING\n");

    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(pipeline));
    g_main_loop_unref(loop);

    return 0;
}
