#include "qgstreamerplayer.h"

#include "qrtspthread.h"
#include "qvideowidget.h"

#include "mainwindow.h"

//#define __720P

bool g_bSend = false;

QGstreamerPlayer::QGstreamerPlayer(QObject *parent) : QObject(parent)
{
    m_bCaptureImage = false;
    m_bWorkingRTSP = false;
    m_bSeeking = false;

    m_nFrameWidth = 0;
    m_nFrameHeight = 0;

    m_DataGST.m_bShow = false;
    m_DataGST.terminate = false;

    m_bPipeStart = false;

    int argc = 0;
    gst_init (&argc, NULL);
    gst_segtrap_set_enabled(FALSE);

    MainWindow* pMainWindow = MainWindow::GetMainWindow();
    connect(this, SIGNAL(ExitCapture()), pMainWindow, SLOT(ExitCapture()));
    connect(this, SIGNAL(ErrorCapture()), pMainWindow, SLOT(ErrorCapture()));





    if(m_DataGST.source==NULL)
    {
        m_DataGST.source = gst_element_factory_make("videotestsrc", "source");
    }
    gst_element_set_state (m_DataGST.source, GST_STATE_READY);

    if(m_DataGST.m_pCaps==NULL)
    {
        m_DataGST.m_pCaps = gst_element_factory_make("capsfilter", "caps");
    }
    gst_element_set_state (m_DataGST.m_pCaps, GST_STATE_READY);

    //if(m_DataGST.queue_src==NULL)
    {
        m_DataGST.queue_src = gst_element_factory_make ("queue", NULL);
    }
    gst_element_set_state (m_DataGST.queue_src, GST_STATE_READY);

    if(m_DataGST.tee==NULL)
    {
        m_DataGST.tee = gst_element_factory_make ("tee", "teename");
    }
    gst_element_set_state (m_DataGST.tee, GST_STATE_READY);

    if(m_DataGST.sink==NULL)
    {
        m_DataGST.sink = gst_element_factory_make("appsink", "test_sink");

        GstAppSinkCallbacks* appsink_callbacks = NULL;
        if(m_DataGST.sink!=NULL)
        {
            appsink_callbacks = (GstAppSinkCallbacks*)malloc(sizeof(GstAppSinkCallbacks));

            appsink_callbacks->eos = app_sink_eos;
            appsink_callbacks->new_preroll = app_sink_new_preroll;
    #ifdef _GSTREAMER_1_0
            appsink_callbacks->new_sample = app_sink_new_sample;
    #else
            appsink_callbacks->new_buffer = app_sink_new_sample;
    #endif
            gst_app_sink_set_callbacks(GST_APP_SINK(m_DataGST.sink), appsink_callbacks, (gpointer)&m_DataGST, NULL);
            g_object_set (m_DataGST.sink, "sync", TRUE, NULL);

            gst_app_sink_set_drop(GST_APP_SINK(m_DataGST.sink), TRUE);
        }
    }
    gst_element_set_state (m_DataGST.sink, GST_STATE_READY);



    m_DataGST.pipeline = gst_pipeline_new("test-pipeline");

    gst_bin_add_many(GST_BIN(m_DataGST.pipeline), m_DataGST.source, m_DataGST.m_pCaps, m_DataGST.queue_src, m_DataGST.tee, m_DataGST.sink, NULL);

    gboolean link_ok;

    link_ok = gst_element_link_many (m_DataGST.source, m_DataGST.m_pCaps, m_DataGST.queue_src, m_DataGST.tee, m_DataGST.sink, NULL);

    if(link_ok==0)
    {
        int a=0;
    }

    GstCaps *caps;

#ifdef __x86_64
    caps = gst_caps_new_simple ("video/x-raw",
            "width", G_TYPE_INT, 1920,
            "height", G_TYPE_INT, 1080,
            "framerate", GST_TYPE_FRACTION, 60, 1,
            "format", G_TYPE_STRING, "UYVY",
            NULL);
#else
    caps = gst_caps_new_simple ("video/x-raw",
            "width", G_TYPE_INT, 1920,
            "height", G_TYPE_INT, 1080,
            "framerate", GST_TYPE_FRACTION, 60, 1,
            "format", G_TYPE_STRING, "UYVY",
            NULL);
#endif

    g_object_set(G_OBJECT(m_DataGST.m_pCaps), "caps", caps, NULL);
}

QGstreamerPlayer::~QGstreamerPlayer()
{
    MainWindow* pMainWindow = MainWindow::GetMainWindow();
    disconnect(this, SIGNAL(ExitCapture()), pMainWindow, SLOT(ExitCapture()));
    disconnect(this, SIGNAL(ErrorCapture()), pMainWindow, SLOT(ErrorCapture()));
    //gst_deinit();
}

bool QGstreamerPlayer::IsPipeStart()
{
    return m_bPipeStart;
}

void QGstreamerPlayer::OpenMovieFile(QString strFilename)
{
    m_nFrameWidth = 0;
    m_nFrameHeight = 0;

    m_DataGST.m_strFilename = strFilename;

    pid_t pid_current = getpid();
    GstBus *bus;
    GstStateChangeReturn ret;
    GstMessage *msg;
    GError *error = NULL;
    gboolean bTrapEnabled = FALSE;

    GstPad *queue_app_pad;
    GstCaps *video_caps;

    m_bTerminate = false;

    /* init */
    int argc = 0;
    gst_init (&argc, NULL);
    gst_segtrap_set_enabled(FALSE);
    bTrapEnabled = gst_segtrap_is_enabled();


    /*
    m_DataGST.source = gst_element_factory_make ("videotestsrc", "source");
    m_DataGST.bin = gst_element_factory_make ("decodebin", "bin");
    m_DataGST.queue_src = gst_element_factory_make ("queue", "queue");
    m_DataGST.sink = gst_element_factory_make ("appsink", "sink");

    m_DataGST.pipeline = gst_pipeline_new ("test-pipeline");





    gst_bin_add_many (GST_BIN (m_DataGST.pipeline), m_DataGST.source, m_DataGST.bin, m_DataGST.sink, NULL);

    if (!gst_element_link (m_DataGST.source, m_DataGST.sink))
    {
        g_printerr ("Elements could not be linked.\n");
        gst_object_unref (m_DataGST.pipeline);
        return;
    }
*/

#ifdef _GSTREAMER_1_0
    //m_DataGST.pipeline = gst_parse_launch("filesrc name=test_src ! queue name=queue_src ! decodebin ! nvvidconv ! video/x-raw,format=I420,width=1920,height=1080 ! queue name=queue_sink ! appsink name=test_sink", &error);
    m_DataGST.pipeline = gst_parse_launch("filesrc name=test_src ! queue name=queue_src ! decodebin ! tee name=teename teename. ! videoconvert ! videoscale ! video/x-raw,format=I420,width=1920,height=1080 ! queue name=queue_sink ! appsink name=test_sink", &error);
#else
    m_DataGST.pipeline = gst_parse_launch("rtspsrc name=test_src location=rtsp://192.168.11.110:8554/test123 latency=0 ! rtph264depay ! queue name=queue_src ! vpudec ! queue name=queue_sink ! appsink name=test_sink max-lateness=-1", &error);
#endif






    if(error!=NULL)
    {
        g_printerr ("Can not Create Pipeline.\n");
        return;
    }



    m_DataGST.source = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "test_src");
    m_DataGST.sink = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "test_sink");

    m_DataGST.queue_src = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "queue_src");
    m_DataGST.queue_sink = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "queue_sink");

    m_DataGST.tee = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "teename");

    m_DataGST.pWidget = m_pVideoWidget;
    m_DataGST.pPlayer = this;

    //test!!!
    //gst_base_sink_set_last_sample_enabled(GST_BASE_SINK(m_DataGST.sink), true);


    g_signal_connect (m_DataGST.source, "pad-added", G_CALLBACK (QGstreamerPlayer::pad_added_handler), &m_DataGST);

    GstPad* pad = gst_element_get_static_pad (m_DataGST.sink, "sink");


    if (!m_DataGST.pipeline || !m_DataGST.source || !m_DataGST.queue_src || !m_DataGST.sink) {
       g_printerr ("Not all elements could be created.\n");
       return;
    }



    if(m_DataGST.queue_src!=NULL)
    {
        g_object_set (m_DataGST.queue_src, "max-size-buffers", 10, NULL);
    }

    if(m_DataGST.source!=NULL)
    {
        g_object_set (m_DataGST.source, "location", strFilename.toStdString().c_str(), NULL);
        //g_object_set (m_DataGST.source, "location", "rtsp://192.168.11.110:8554/test123", NULL);
    }

    if(m_DataGST.sink!=NULL)
    {
        GstAppSinkCallbacks* appsink_callbacks = (GstAppSinkCallbacks*)malloc(sizeof(GstAppSinkCallbacks));

        appsink_callbacks->eos = app_sink_eos;
        appsink_callbacks->new_preroll = app_sink_new_preroll;
#ifdef _GSTREAMER_1_0
        appsink_callbacks->new_sample = app_sink_new_sample;
#else
        appsink_callbacks->new_buffer = app_sink_new_sample;
#endif

        //test

        gst_app_sink_set_callbacks(GST_APP_SINK(m_DataGST.sink), appsink_callbacks, (gpointer)&m_DataGST, NULL);

        //g_signal_connect (m_DataGST.source, "new-buffer", G_CALLBACK (new_buffer), &m_DataGST);

        g_object_set (m_DataGST.sink, "sync", TRUE, NULL);

        gst_app_sink_set_drop(GST_APP_SINK(m_DataGST.sink), TRUE);
    }












    //queue_app_pad = gst_element_get_static_pad (m_DataGST.app_queue, "sink");

    //sigint_setup();




    ret = gst_element_set_state (m_DataGST.pipeline, GST_STATE_PLAYING);
    //ret = gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (m_DataGST.pipeline);
        return;
    }

    bus = gst_element_get_bus (m_DataGST.pipeline);

    int64_t tEnd=0;
    do {
        msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
            (GstMessageType)(GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STEP_DONE | GST_MESSAGE_STEP_START | GST_MESSAGE_ASYNC_DONE));

        usleep(100);
        if(tEnd==0)
        {
            gst_element_query_duration (m_DataGST.pipeline, GST_FORMAT_TIME, &tEnd);
            if(tEnd!=0)
            {
                int nHour = 0;
                int nMinute = 0;
                int nSec = 0;

                int nMilliSec = tEnd/(1000*1000);
                nSec = nMilliSec/1000;
                nMinute = nSec/60;
                nHour = nMinute/60;

                nSec = nSec%60;
                nMinute = nMinute%60;
                nHour = nHour;
                nMilliSec = (nMilliSec/1000)%1000;
                qDebug() << "Duration: " << nHour << ":" << nMinute << ":" << nSec << ":" << nMilliSec ;

                //m_bTerminate = true;

                GstState state;
                GstState pending;

                gst_element_get_state(m_DataGST.pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
            }
        }
        /* Parse message */
        if (msg != NULL) {
          GError *err;
          gchar *debug_info;

          switch (GST_MESSAGE_TYPE (msg)) {
            case GST_MESSAGE_ERROR:
              gst_message_parse_error (msg, &err, &debug_info);
              g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
              g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
              g_clear_error (&err);
              g_free (debug_info);
              m_bTerminate = true;
              break;

            case GST_MESSAGE_EOS:
              g_print ("End-Of-Stream reached.\n");

          {

                  m_bTerminate = true;

          }


              break;
            case GST_MESSAGE_STATE_CHANGED:
              /* We are only interested in state-changed messages from the pipeline */
              if (GST_MESSAGE_SRC (msg) == GST_OBJECT (m_DataGST.pipeline)) {
                GstState old_state, new_state, pending_state;
                gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
                g_print ("Pipeline state changed from %s to %s:\n",
                    gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));

                if(new_state==GST_STATE_PAUSED && old_state==GST_STATE_PLAYING)
                {
                    //m_bTerminate = true;
                }
              }
              break;

            case GST_MESSAGE_STEP_DONE:
            {
                GstFormat format;
                guint64 amount;
                gdouble rate;
                gboolean flush, intermediate;
                guint64 duration;
                gboolean eos;

                gst_message_parse_step_done (msg, &format, &amount, &rate,
                &flush, &intermediate, &duration, &eos);

                app_sink_new_sample((GstAppSink *)m_DataGST.sink, &m_DataGST);

            }
            break;

          case GST_MESSAGE_STEP_START:
          {
              int a=0;
          }
              break;

          case GST_MESSAGE_ASYNC_DONE:
          {
              qDebug() << "Async Done";
              //app_sink_new_sample((GstAppSink *)m_DataGST.sink, &m_DataGST);


              m_bSeeking = false;
          }
              break;





            default:
              /* We should not reach here */
              g_printerr ("Unexpected message received.\n");
              break;
          }





          gst_message_unref (msg);
        }
    } while (m_bTerminate==false);




    SendFinished();
}


bool QGstreamerPlayer::IsWorkingRTSP()
{
    return m_bWorkingRTSP;
}

int QGstreamerPlayer::OpenCamera(QString strVideoDevice)
{
    return OpenCamera2(strVideoDevice);

    m_bWorkingRTSP = true;
    m_nFrameWidth = 0;
    m_nFrameHeight = 0;

    m_DataGST.m_strFilename = strVideoDevice;

    GstBus *bus;
    GstStateChangeReturn ret;
    GstMessage *msg;
    GError *error = NULL;
    gboolean bTrapEnabled = FALSE;

    m_bTerminate = false;

    /* init */    
    bTrapEnabled = gst_segtrap_is_enabled();

    QString strTest = QString("v4l2src device=/dev/video0 name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! videorate name=test_videorate ! queue name=queue_name1 ! timeoverlay name=test_timeoverlay ! tee name=teename ! queue name=queue_name2 ! appsink name=test_sink");

#ifdef _GSTREAMER_1_0
#ifdef __x86_64
    strTest = QString("videotestsrc name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! tee name=teename ! appsink name=test_sink");



#else
    strTest = QString("videotestsrc name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! tee name=teename ! appsink name=test_sink");    

#endif
#else
    m_DataGST.pipeline = gst_parse_launch("rtspsrc name=test_src location=rtsp://192.168.11.110:8554/test123 latency=0 ! rtph264depay ! queue name=queue_src ! vpudec ! queue name=queue_sink ! appsink name=test_sink max-lateness=-1", &error);
#endif

    if(error!=NULL)
    {
        g_printerr ("Can not Create Pipeline.\n");
        m_bWorkingRTSP = false;
        ErrorCapture();
        return -1;
    }

    m_DataGST.pWidget = m_pVideoWidget;
    m_DataGST.pPlayer = this;
    m_DataGST.m_bShow = true;

    //test!!!
    g_signal_connect (m_DataGST.source, "pad-added", G_CALLBACK (QGstreamerPlayer::pad_added_handler), &m_DataGST);

    GstPad* pad = gst_element_get_static_pad (m_DataGST.sink, "sink");

    //if (!m_DataGST.pipeline || !m_DataGST.source || !m_DataGST.queue_src || !m_DataGST.sink) {
    if (!m_DataGST.pipeline || !m_DataGST.source || !m_DataGST.sink) {
       g_printerr ("Not all elements could be created.\n");
       m_bWorkingRTSP = false;
       ErrorCapture();
       return -2;
    }

    if(m_DataGST.queue_src!=NULL)
    {
        //g_object_set (m_DataGST.queue_src, "max-size-buffers", 10, NULL);
    }

    if(m_DataGST.source!=NULL)
    {
        //g_object_set (m_DataGST.source, "device", strVideoDevice.toStdString().c_str(), NULL);

    }



    ret = gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (m_DataGST.pipeline);
        m_bPipeStart = false;
        m_bWorkingRTSP = false;
        ExitCapture();
        return -3;
    }

    bus = gst_element_get_bus (m_DataGST.pipeline);

    m_bPipeStart = true;

    int64_t tEnd=0;
    do {
        msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
            (GstMessageType)(GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STEP_DONE | GST_MESSAGE_STEP_START | GST_MESSAGE_ASYNC_DONE));


        if(tEnd==0)
        {
            gst_element_query_duration (m_DataGST.pipeline, GST_FORMAT_TIME, &tEnd);
            if(tEnd!=0)
            {
                int nHour = 0;
                int nMinute = 0;
                int nSec = 0;

                int nMilliSec = tEnd/(1000*1000);
                nSec = nMilliSec/1000;
                nMinute = nSec/60;
                nHour = nMinute/60;

                nSec = nSec%60;
                nMinute = nMinute%60;
                nHour = nHour;
                nMilliSec = (nMilliSec/1000)%1000;
                qDebug() << "Duration: " << nHour << ":" << nMinute << ":" << nSec << ":" << nMilliSec ;

                //m_bTerminate = true;

                GstState state;
                GstState pending;

                gst_element_get_state(m_DataGST.pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
            }
        }
        /* Parse message */
        if (msg != NULL) {
          GError *err;
          gchar *debug_info;

          switch (GST_MESSAGE_TYPE (msg)) {
            case GST_MESSAGE_ERROR:
              gst_message_parse_error (msg, &err, &debug_info);
              g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
              g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
              g_clear_error (&err);
              g_free (debug_info);
              m_bTerminate = true;
              break;

            case GST_MESSAGE_EOS:
              g_print ("End-Of-Stream reached.\n");

          {

              //gst_message_unref (msg);
                  m_bTerminate = true;

          }


              break;
            case GST_MESSAGE_STATE_CHANGED:
              /* We are only interested in state-changed messages from the pipeline */
              GstState old_state, new_state, pending_state;
              gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);

              if (GST_MESSAGE_SRC (msg) == GST_OBJECT (m_DataGST.pipeline)) {

                g_print ("Pipeline state changed from %s to %s:\n",
                    gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));

                if(new_state==GST_STATE_PAUSED && old_state==GST_STATE_PLAYING)
                {
                    //m_bTerminate = true;
                    int a=0;
                    ExitCapture();
                }
                else if(new_state==GST_STATE_PLAYING && old_state==GST_STATE_PAUSED)
                {
                    int a=0;
                }
              }
              break;

            case GST_MESSAGE_STEP_DONE:
            {
                GstFormat format;
                guint64 amount;
                gdouble rate;
                gboolean flush, intermediate;
                guint64 duration;
                gboolean eos;

                gst_message_parse_step_done (msg, &format, &amount, &rate,
                &flush, &intermediate, &duration, &eos);

                app_sink_new_sample((GstAppSink *)m_DataGST.sink, &m_DataGST);

            }
            break;

          case GST_MESSAGE_STEP_START:
          {
              int a=0;
          }
              break;

          case GST_MESSAGE_ASYNC_DONE:
          {
              qDebug() << "Async Done";
              //app_sink_new_sample((GstAppSink *)m_DataGST.sink, &m_DataGST);


              m_bSeeking = false;
          }
              break;





            default:
              /* We should not reach here */
              g_printerr ("Unexpected message received.\n");
              break;
          }


          gst_message_unref (msg);

          if(m_DataGST.terminate==true)
          {
              m_bTerminate = true;
          }
        }
    } while (m_bTerminate==false);


    /*
    gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    m_bPipeStart = false;
    m_bTerminate = false;
    m_DataGST.terminate = false;
    //gst_deinit();

    //gst_object_ref(caps);
    //free(caps);

    ExitCapture();

    return 0;
    */

    if(m_DataGST.pipeline!=NULL)
    {
        gst_element_set_state (m_DataGST.pipeline, GST_STATE_NULL);
        gst_object_unref (m_DataGST.pipeline);
        m_DataGST.pipeline = NULL;
    }

    if(m_DataGST.source!=NULL)
    {
        //gst_element_set_state (m_DataGST.source, GST_STATE_NULL);
        //gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.source);
        gst_object_unref (m_DataGST.source);
        m_DataGST.source = NULL;
    }

    if(m_DataGST.tee!=NULL)
    {
        //gst_element_set_state (m_DataGST.tee, GST_STATE_NULL);
        //gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.tee);

        gst_object_unref (m_DataGST.tee);
        m_DataGST.tee = NULL;
    }

    if(m_DataGST.queue_src!=NULL)
    {
        //gst_element_set_state (m_DataGST.queue_src, GST_STATE_NULL);
        //gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.queue_src);

        gst_object_unref (m_DataGST.queue_src);
        m_DataGST.queue_src = NULL;
    }

    if(m_DataGST.queue_sink!=NULL)
    {
        //gst_element_set_state (m_DataGST.queue_sink, GST_STATE_NULL);
        //gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.queue_sink);

        gst_object_unref (m_DataGST.queue_sink);
        m_DataGST.queue_sink = NULL;
    }

    if(m_DataGST.sink!=NULL)
    {
        //gst_element_set_state (m_DataGST.sink, GST_STATE_NULL);
        //gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.sink);

        gst_object_unref (m_DataGST.sink);
        m_DataGST.sink = NULL;
    }




    gst_object_unref (bus);

    //free(appsink_callbacks);

    m_bPipeStart = false;
    m_bTerminate = false;
    m_DataGST.terminate = false;
    //gst_deinit();

    //gst_object_ref(caps);
    //free(caps);

    //ExitCapture();

    return 0;
}

int QGstreamerPlayer::OpenCamera2(QString strVideoDevice)
{
    m_bWorkingRTSP = true;
    m_nFrameWidth = 0;
    m_nFrameHeight = 0;

    m_DataGST.m_strFilename = strVideoDevice;

    pid_t pid_current = getpid();
    GstBus *bus;
    GstStateChangeReturn ret;
    GstMessage *msg;
    GError *error = NULL;
    gboolean bTrapEnabled = FALSE;

    GstPad *queue_app_pad;
    GstCaps *video_caps;

    m_bTerminate = false;

    /* init */
    //int argc = 0;
    //gst_init (&argc, NULL);
    //gst_segtrap_set_enabled(FALSE);
    bTrapEnabled = gst_segtrap_is_enabled();

    QSize nSize = m_pVideoWidget->size();

    QString strTest = QString("v4l2src device=/dev/video0 name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! videorate name=test_videorate ! queue name=queue_name1 ! timeoverlay name=test_timeoverlay ! tee name=teename ! queue name=queue_name2 ! appsink name=test_sink");

#ifdef _GSTREAMER_1_0
#ifdef __x86_64
    strTest = QString("videotestsrc name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! videorate name=test_videorate ! queue name=queue_name1 ! timeoverlay name=test_timeoverlay ! tee name=teename ! queue name=queue_name2 ! appsink name=test_sink");
#ifdef __720P
    m_DataGST.pipeline = gst_parse_launch("v4l2src name=test_src ! video/x-raw,format=(string)YUY2,width=1280,height=720,framerate=(fraction)60/1 ! queue name=queue_src ! tee name=teename teename. ! queue ! appsink name=test_sink", &error);
#else
    //m_DataGST.pipeline = gst_parse_launch("v4l2src name=test_src ! 'video/x-raw,format=(string)YUY2,width=1920,height=1080,framerate=(fraction)120/1' ! queue name=queue_src ! tee name=teename teename. ! queue ! appsink name=test_sink", &error);

    //m_DataGST.pipeline = gst_parse_launch("videotestsrc name=test_src ! video/x-raw,format=(string)YUY2,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! videorate name=test_videorate ! clockoverlay ! appsink name=test_sink", &error);

    m_DataGST.pipeline = gst_parse_launch(strTest.toStdString().c_str(), &error);
#endif

#else

    //strTest = QString("videotestsrc name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! videorate name=test_videorate ! queue name=queue_name1 ! timeoverlay name=test_timeoverlay ! tee name=teename ! queue name=queue_name2 ! appsink name=test_sink");

    strTest = QString("v4l2src name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! tee name=teename ! queue name=queue_sink ! appsink name=test_sink");

    m_DataGST.pipeline = gst_parse_launch(strTest.toStdString().c_str(), &error);
    /*
    m_DataGST.pipeline = gst_parse_launch("v4l2src name=test_src ! video/x-raw,width=1920,height=1080,framerate=(fraction)60/1,format=(string)YUY2 ! queue name=queue_src ! tee name=teename teename. ! queue ! videoconvert ! queue ! appsink name=test_sink ", &error);
    //m_DataGST.pipeline = gst_parse_launch("videotestsrc name=test_src ! video/x-raw,format=(string)UYVY,width=1920,height=1080,framerate=(fraction)60/1 ! queue name=queue_src ! appsink name=test_sink", &error);
    */

#endif
#else
    m_DataGST.pipeline = gst_parse_launch("rtspsrc name=test_src location=rtsp://192.168.11.110:8554/test123 latency=0 ! rtph264depay ! queue name=queue_src ! vpudec ! queue name=queue_sink ! appsink name=test_sink max-lateness=-1", &error);
#endif

    if(error!=NULL)
    {
        g_printerr ("Can not Create Pipeline.\n");
        m_bWorkingRTSP = false;
        ErrorCapture();
        return -1;
    }



    /*
    GstElement* pVideoRate = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "test_videorate");
    GstElement* pTimeOverlay = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "test_timeoverlay");
    GstElement* pQueue1 = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "queue_name1");
    GstElement* pQueue2 = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "queue_name2");
    */


    m_DataGST.source = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "test_src");
    m_DataGST.sink = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "test_sink");

    m_DataGST.queue_src = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "queue_src");
    m_DataGST.queue_sink = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "queue_sink");

    m_DataGST.tee = gst_bin_get_by_name(GST_BIN(m_DataGST.pipeline), "teename");

    m_DataGST.pWidget = m_pVideoWidget;
    m_DataGST.pPlayer = this;

    m_DataGST.m_bShow = true;

    //test!!!
    //gst_base_sink_set_last_sample_enabled(GST_BASE_SINK(m_DataGST.sink), true);


    g_signal_connect (m_DataGST.source, "pad-added", G_CALLBACK (QGstreamerPlayer::pad_added_handler), &m_DataGST);

    GstPad* pad = gst_element_get_static_pad (m_DataGST.sink, "sink");


    //if (!m_DataGST.pipeline || !m_DataGST.source || !m_DataGST.queue_src || !m_DataGST.sink) {
    if (!m_DataGST.pipeline || !m_DataGST.source || !m_DataGST.sink) {
       g_printerr ("Not all elements could be created.\n");
       m_bWorkingRTSP = false;
       ErrorCapture();
       return -2;
    }



    if(m_DataGST.queue_src!=NULL)
    {
        g_object_set (m_DataGST.queue_src, "max-size-buffers", 10, NULL);
    }

    if(m_DataGST.source!=NULL)
    {
        //g_object_set (m_DataGST.source, "device", strVideoDevice.toStdString().c_str(), NULL);

    }

    GstAppSinkCallbacks* appsink_callbacks = NULL;
    if(m_DataGST.sink!=NULL)
    {
        appsink_callbacks = (GstAppSinkCallbacks*)malloc(sizeof(GstAppSinkCallbacks));

        appsink_callbacks->eos = app_sink_eos;
        appsink_callbacks->new_preroll = app_sink_new_preroll;
#ifdef _GSTREAMER_1_0
        appsink_callbacks->new_sample = app_sink_new_sample;
#else
        appsink_callbacks->new_buffer = app_sink_new_sample;
#endif

        //test

        gst_app_sink_set_callbacks(GST_APP_SINK(m_DataGST.sink), appsink_callbacks, (gpointer)&m_DataGST, NULL);

        //g_signal_connect (m_DataGST.source, "new-buffer", G_CALLBACK (new_buffer), &m_DataGST);

        //g_object_set (m_DataGST.sink, "sync", FALSE, NULL);
        g_object_set (m_DataGST.sink, "sync", TRUE, NULL);

        gst_app_sink_set_drop(GST_APP_SINK(m_DataGST.sink), TRUE);
        //gst_app_sink_set_drop(GST_APP_SINK(m_DataGST.sink), FALSE);

    }












    //queue_app_pad = gst_element_get_static_pad (m_DataGST.app_queue, "sink");

    //sigint_setup();




    //ret = gst_element_set_state (m_DataGST.pipeline, GST_STATE_PLAYING);
    ret = gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (m_DataGST.pipeline);
        m_bPipeStart = false;
        m_bWorkingRTSP = false;
        //ErrorCapture();
        ExitCapture();
        return -3;
    }

    bus = gst_element_get_bus (m_DataGST.pipeline);

    m_bPipeStart = true;

    int64_t tEnd=0;
    do {
        msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
            (GstMessageType)(GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STEP_DONE | GST_MESSAGE_STEP_START | GST_MESSAGE_ASYNC_DONE));


        if(tEnd==0)
        {
            gst_element_query_duration (m_DataGST.pipeline, GST_FORMAT_TIME, &tEnd);
            if(tEnd!=0)
            {
                int nHour = 0;
                int nMinute = 0;
                int nSec = 0;

                int nMilliSec = tEnd/(1000*1000);
                nSec = nMilliSec/1000;
                nMinute = nSec/60;
                nHour = nMinute/60;

                nSec = nSec%60;
                nMinute = nMinute%60;
                nHour = nHour;
                nMilliSec = (nMilliSec/1000)%1000;
                qDebug() << "Duration: " << nHour << ":" << nMinute << ":" << nSec << ":" << nMilliSec ;

                //m_bTerminate = true;

                GstState state;
                GstState pending;

                gst_element_get_state(m_DataGST.pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
            }
        }
        /* Parse message */
        if (msg != NULL) {
          GError *err;
          gchar *debug_info;

          switch (GST_MESSAGE_TYPE (msg)) {
            case GST_MESSAGE_ERROR:
              gst_message_parse_error (msg, &err, &debug_info);
              g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
              g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
              g_clear_error (&err);
              g_free (debug_info);
              m_bTerminate = true;
              break;

            case GST_MESSAGE_EOS:
              g_print ("End-Of-Stream reached.\n");

          {

              //gst_message_unref (msg);
                  m_bTerminate = true;

          }


              break;
            case GST_MESSAGE_STATE_CHANGED:
              /* We are only interested in state-changed messages from the pipeline */
              GstState old_state, new_state, pending_state;
              gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);

              if (GST_MESSAGE_SRC (msg) == GST_OBJECT (m_DataGST.pipeline)) {

                g_print ("Pipeline state changed from %s to %s:\n",
                    gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));

                if(new_state==GST_STATE_PAUSED && old_state==GST_STATE_PLAYING)
                {
                    //m_bTerminate = true;
                }
              }
              break;

            case GST_MESSAGE_STEP_DONE:
            {
                GstFormat format;
                guint64 amount;
                gdouble rate;
                gboolean flush, intermediate;
                guint64 duration;
                gboolean eos;

                gst_message_parse_step_done (msg, &format, &amount, &rate,
                &flush, &intermediate, &duration, &eos);

                app_sink_new_sample((GstAppSink *)m_DataGST.sink, &m_DataGST);

            }
            break;

          case GST_MESSAGE_STEP_START:
          {
              int a=0;
          }
              break;

          case GST_MESSAGE_ASYNC_DONE:
          {
              qDebug() << "Async Done";
              //app_sink_new_sample((GstAppSink *)m_DataGST.sink, &m_DataGST);


              m_bSeeking = false;
          }
              break;





            default:
              /* We should not reach here */
              g_printerr ("Unexpected message received.\n");
              break;
          }


          gst_message_unref (msg);

          if(m_DataGST.terminate==true)
          {
              m_bTerminate = true;
          }
        }
    } while (m_bTerminate==false);

    /*
    if(pVideoRate!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), pVideoRate);
        gst_element_set_state (pVideoRate, GST_STATE_NULL);
        gst_object_unref (pVideoRate);
    }

    if(pTimeOverlay!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), pTimeOverlay);
        gst_element_set_state (pTimeOverlay, GST_STATE_NULL);
        gst_object_unref (pTimeOverlay);
    }


    if(pQueue1!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), pQueue1);
        gst_element_set_state (pQueue1, GST_STATE_NULL);
        gst_object_unref (pQueue1);
    }

    if(pQueue2!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), pQueue2);
        gst_element_set_state (pQueue2, GST_STATE_NULL);
        gst_object_unref (pQueue2);
    }
    */




    if(m_DataGST.source!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.source);
        gst_element_set_state (m_DataGST.source, GST_STATE_NULL);
        gst_object_unref (m_DataGST.source);
    }

    if(m_DataGST.tee!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.tee);
        gst_element_set_state (m_DataGST.tee, GST_STATE_NULL);
        gst_object_unref (m_DataGST.tee);
    }




    if(m_DataGST.queue_src!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.queue_src);
        gst_element_set_state (m_DataGST.queue_src, GST_STATE_NULL);
        gst_object_unref (m_DataGST.queue_src);
    }


    if(m_DataGST.queue_sink!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.queue_sink);
        gst_element_set_state (m_DataGST.queue_sink, GST_STATE_NULL);
        gst_object_unref (m_DataGST.queue_sink);
    }


    if(m_DataGST.sink!=NULL)
    {
        gst_bin_remove (GST_BIN (m_DataGST.pipeline), m_DataGST.sink);
        gst_element_set_state (m_DataGST.sink, GST_STATE_NULL);
        gst_object_unref (m_DataGST.sink);
    }


    if(m_DataGST.pipeline!=NULL)
    {
        gst_element_set_state (m_DataGST.pipeline, GST_STATE_NULL);
        gst_object_unref (m_DataGST.pipeline);
    }



    gst_object_unref (bus);

    free(appsink_callbacks);

    m_bPipeStart = false;
    m_bTerminate = false;
    m_DataGST.terminate = false;
    //gst_deinit();




    ExitCapture();

    return 0;
}

void QGstreamerPlayer::app_sink_eos(GstAppSink *sink, gpointer user_data)
{
    GST_CustomData* pData = (GST_CustomData*)user_data;

    pData->terminate = true;

    qDebug() << "Enter EOS";

    if(pData->m_pSink!=NULL)
    {
        if(pData->m_pSink->removing==TRUE)
        {

            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pQueue);
            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pRate);
            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pCaps);
            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pConvert);            
            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pSink);
            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pEncoder);
            gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pParser);

            gst_element_set_state (pData->m_pSink->m_pQueue, GST_STATE_NULL);
            gst_element_set_state (pData->m_pSink->m_pRate, GST_STATE_NULL);
            gst_element_set_state (pData->m_pSink->m_pCaps, GST_STATE_NULL);
            gst_element_set_state (pData->m_pSink->m_pConvert, GST_STATE_NULL);
            gst_element_set_state (pData->m_pSink->m_pSink, GST_STATE_NULL);
            gst_element_set_state (pData->m_pSink->m_pEncoder, GST_STATE_NULL);
            gst_element_set_state (pData->m_pSink->m_pParser, GST_STATE_NULL);


            gst_object_unref (pData->m_pSink->m_pQueue);
            gst_object_unref (pData->m_pSink->m_pRate);
            gst_object_unref (pData->m_pSink->m_pCaps);
            gst_object_unref (pData->m_pSink->m_pConvert);
            gst_object_unref (pData->m_pSink->m_pSink);
            gst_object_unref (pData->m_pSink->m_pEncoder);
            gst_object_unref (pData->m_pSink->m_pParser);

            gst_element_release_request_pad (pData->tee, pData->m_pSink->m_pTeePad);
            gst_object_unref (pData->m_pSink->m_pTeePad);

            g_free(pData->m_pSink);
            pData->m_pSink = NULL;
        }
        //else
        {

        }

    }

}

GstFlowReturn QGstreamerPlayer::app_sink_new_preroll(GstAppSink *sink, gpointer user_data)
{
    qDebug() << "preroll" ;

    GST_CustomData* pd = (GST_CustomData*)user_data;
    GstSample* sample = NULL;



    int nState = pd->pThread->GetState();
    if(nState>0)
    {
        //return GST_FLOW_OK;
    }

    if(g_bSend==true)
    {
        //qDebug() << "track";
        //gst_element_set_state (pd->pipeline, GST_STATE_PAUSED);
    }

    //sample = gst_app_sink_pull_sample(sink);
    sample = gst_app_sink_pull_preroll(sink);

    if(sample==NULL)
    {
        qDebug() << "app_sink_new_proroll ERROR";
        return GST_FLOW_OK;
    }

    QGstreamerPlayer* pPlayer = (QGstreamerPlayer*)pd->pPlayer;

    if(pPlayer->GetFrameWidth()==0 || pPlayer->GetFrameHeight()==0)
    {
        GstCaps* pCaps = NULL;
        pCaps = gst_sample_get_caps(sample);
        GstStructure* s = gst_caps_get_structure (pCaps, 0);
        gint width, height;
        int res = gst_structure_get_int (s, "width", &width)  | gst_structure_get_int (s, "height", &height);

        pPlayer->SetFrameWidth(width);
        pPlayer->SetFrameHeight(height);

        pd->pWidget->SetTextureSize(width, height);


    }

    gboolean bEnabledLast = gst_base_sink_is_last_sample_enabled(GST_BASE_SINK(sink));



    GstBuffer* buffer = gst_sample_get_buffer(sample);

    GstMemory* memory = gst_buffer_get_all_memory(buffer);
    GstMapInfo map_info;

    if(! gst_memory_map(memory, &map_info, GST_MAP_READ)) {
      gst_memory_unref(memory);
      gst_sample_unref(sample);

      usleep(10*1000);
      return GST_FLOW_ERROR;
    }

    guint8* pData = map_info.data;

    if(pd->pWidget!=NULL)
    {
        if(map_info.size>4096)
        {
            //g_pTestDT->
            pd->pWidget->SetTextureData(pData, map_info.size);

            /*

            FILE* pFile = NULL;
            pFile = fopen("test.data", "wb");
            fwrite(pData, 1280*720*1.5, 1, pFile);
            fclose(pFile);
            */

        }
        else
        {
            int a=0;
        }

    }
    else
    {
        FILE* pFile = NULL;
        pFile = fopen("test.data", "wb");

        fwrite(pData, 1280*720, 1, pFile);

        fclose(pFile);
    }



    if(sample == NULL) {
      return GST_FLOW_ERROR;
    }

    gst_memory_unmap(memory, &map_info);
    gst_memory_unref(memory);
    gst_sample_unref(sample);

    usleep(1*1000);

    return GST_FLOW_OK;
}


GstFlowReturn QGstreamerPlayer::app_sink_new_sample(GstAppSink *sink, gpointer user_data)
{

  GST_CustomData* pd = (GST_CustomData*)user_data;
  GstSample* sample = NULL;

  if(pd->m_bShow==false)
  {
      pd->terminate = true;
      //gst_element_set_state (pd->pipeline, GST_STATE_PAUSED);

      gst_bin_remove (GST_BIN (pd->pipeline), pd->source);
      gst_element_set_state (pd->source, GST_STATE_NULL);
      gst_object_unref (pd->source);

      gst_element_set_state (pd->pipeline, GST_STATE_NULL);
      gst_object_unref (pd->pipeline);

      //gst_object_unref (pd->bus);

      //m_bTerminate = false;
      //m_DataGST.terminate = false;

      return GST_FLOW_EOS;
  }


  //qDebug() << "app_sink_new_sample";

  int nState = pd->pThread->GetState();
  if(nState>0)
  {
      //return GST_FLOW_OK;
  }

  if(g_bSend==true)
  {
      //qDebug() << "track";
      //gst_element_set_state (pd->pipeline, GST_STATE_PAUSED);
  }

  sample = gst_app_sink_pull_sample(sink);
  if(sample==NULL)
  {
      qDebug() << "app_sink_new_sample ERROR";
      return GST_FLOW_CUSTOM_ERROR;
  }

  QGstreamerPlayer* pPlayer = (QGstreamerPlayer*)pd->pPlayer;

  if(pPlayer->GetFrameWidth()==0 || pPlayer->GetFrameHeight()==0)
  {
      GstCaps* pCaps = NULL;
      pCaps = gst_sample_get_caps(sample);
      GstStructure* s = gst_caps_get_structure (pCaps, 0);
      gint width, height;
      int res = gst_structure_get_int (s, "width", &width)  | gst_structure_get_int (s, "height", &height);

      pPlayer->SetFrameWidth(width);
      pPlayer->SetFrameHeight(height);

      pd->pWidget->SetTextureSize(width, height);

  }

  //sample = gst_app_sink_pull_preroll(sink);
  gboolean bEnabledLast = gst_base_sink_is_last_sample_enabled(GST_BASE_SINK(sink));



  GstBuffer* buffer = gst_sample_get_buffer(sample);

  GstMemory* memory = gst_buffer_get_all_memory(buffer);
  GstMapInfo map_info;

  if(! gst_memory_map(memory, &map_info, GST_MAP_READ)) {
    gst_memory_unref(memory);
    gst_sample_unref(sample);

    usleep(10*1000);
    return GST_FLOW_ERROR;
  }

  guint8* pData = map_info.data;

  if(pd->pWidget!=NULL && pd->m_bShow==true)
  {
      if(map_info.size>4096)
      {
          //g_pTestDT->
          if(pd->pWidget->isVisible()==false)
          {
              int a=0;
          }
          if(pd->pWidget->isActiveWindow()==false)
          {
              int a=0;
          }
          pd->pWidget->SetTextureData(pData, map_info.size);



          /*
          FILE* pFile = NULL;
          pFile = fopen("test.data", "wb");
          //fwrite(pData, 1280*720*1.5, 1, pFile);
          fwrite(pData, 1920*1080*2, 1, pFile);
          fclose(pFile);
          */


      }
      else
      {
          int a=0;
      }

  }
  else
  {
      if(pd->m_bShow==false)
      {
          int a=0;
      }
      /*
      FILE* pFile = NULL;
      pFile = fopen("test.data", "wb");

      fwrite(pData, 1280*720, 1, pFile);

      fclose(pFile);
      */
  }



  if(sample == NULL) {
    return GST_FLOW_ERROR;
  }

  gst_memory_unmap(memory, &map_info);
  gst_memory_unref(memory);
  gst_sample_unref(sample);

  //usleep(1*1000);

  return GST_FLOW_OK;
}

void QGstreamerPlayer::pad_added_handler (GstElement *src, GstPad *new_pad, GST_CustomData *data) {
  GstPad *sink_pad = gst_element_get_static_pad (data->sink, "sink");
  GstPadLinkReturn ret;
  GstCaps *new_pad_caps = NULL;
  GstStructure *new_pad_struct = NULL;
  const gchar *new_pad_type = NULL;

  g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src));

  /* If our converter is already linked, we have nothing to do here */
  if (gst_pad_is_linked (sink_pad)) {
    g_print ("  We are already linked. Ignoring.\n");
    goto exit;
  }

  /* Check the new pad's type */
  new_pad_caps = gst_pad_get_allowed_caps (new_pad);
  new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
  new_pad_type = gst_structure_get_name (new_pad_struct);
  if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
    g_print ("  It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
    goto exit;
  }

  /* Attempt the link */
  ret = gst_pad_link (new_pad, sink_pad);
  if (GST_PAD_LINK_FAILED (ret)) {
    g_print ("  Type is '%s' but link failed.\n", new_pad_type);
  } else {
    g_print ("  Link succeeded (type '%s').\n", new_pad_type);
  }

exit:
  /* Unreference the new pad's caps, if we got them */
  if (new_pad_caps != NULL)
    gst_caps_unref (new_pad_caps);

  /* Unreference the sink pad */
  gst_object_unref (sink_pad);
}

void QGstreamerPlayer::SetThread(QRTSPThread *pThread)
{
    m_DataGST.pThread = pThread;
}

void QGstreamerPlayer::SetVideoWidget(QVideoWidget *pWidget)
{
    m_pVideoWidget = pWidget;
    m_DataGST.pWidget = pWidget;
}

void QGstreamerPlayer::Seek(gint64 nPos)
{
    GstEvent *seek_event;

    m_DataGST.rate = 1.0;

    if(m_bSeeking==false)
    {
        m_nSeekPos = nPos;
        qDebug() << nPos;

        m_bSeeking = true;

        //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PLAYING);

        gst_element_seek_simple(m_DataGST.pipeline, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), nPos);
        //seek_event = gst_event_new_seek (m_DataGST.rate, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_KEY_UNIT),
        //seek_event = gst_event_new_seek (m_DataGST.rate, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
         //       GST_SEEK_TYPE_SET, nPos, GST_SEEK_TYPE_END, nPos);

        //gboolean bSend = gst_element_send_event (m_DataGST.pipeline, seek_event);

        //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
        //gst_element_send_event (m_DataGST.pipeline, gst_event_new_step (GST_FORMAT_BUFFERS, 1, m_DataGST.rate, TRUE, FALSE));

        g_bSend = true;
    }
    else
    {
        m_ListSeeking.push_back(nPos);
    }


}

void QGstreamerPlayer::Pause()
{
    gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
}

void QGstreamerPlayer::StartRTSP()
{
    gst_element_set_state (m_DataGST.pipeline, GST_STATE_PLAYING);
    m_DataGST.m_bShow = true;

    m_pVideoWidget->setVisible(true);
    m_pVideoWidget->activateWindow();
    m_pVideoWidget->setFocus(Qt::ActiveWindowFocusReason);
    m_pVideoWidget->update();

    return;


}

void QGstreamerPlayer::StopRTSP()
{
    gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    return;
    //m_DataGST.m_bShow = false;
    m_DataGST.terminate = true;
    //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    gst_element_send_event (m_DataGST.pipeline, gst_event_new_eos ());
    return;

    //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);


}

void QGstreamerPlayer::exitRTSP()
{
    m_DataGST.m_bShow = false;

    m_bTerminate = true;

    gst_element_set_state (m_DataGST.pipeline, GST_STATE_PLAYING);
    usleep(100*1000);
    gst_element_send_event (m_DataGST.pipeline, gst_event_new_eos ());
    return;

    //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);


}

void QGstreamerPlayer::Resume()
{
    gst_element_set_state (m_DataGST.pipeline, GST_STATE_PLAYING);
}

bool QGstreamerPlayer::IsSeeking()
{
    return m_bSeeking;
}

gint64 QGstreamerPlayer::GetSeekPos()
{
    return m_nSeekPos;
}

void QGstreamerPlayer::SetFrameWidth(int nWidth)
{
    m_nFrameWidth = nWidth;
}

void QGstreamerPlayer::SetFrameHeight(int nHeight)
{
    m_nFrameHeight = nHeight;
}

int QGstreamerPlayer::GetFrameWidth()
{
    return m_nFrameWidth;
}

int QGstreamerPlayer::GetFrameHeight()
{
    return m_nFrameHeight;
}

void QGstreamerPlayer::SaveFile(QString strFilename)
{

#ifdef __x86_64
    CustomDataSink *sink = g_new0 (CustomDataSink, 1);
    GstPad *sinkpad;
    GstPadTemplate *templ;

    //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    templ = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (m_DataGST.tee), "src_%u");

    g_print ("add\n");

    sink->m_pTeePad = gst_element_request_pad (m_DataGST.tee, templ, NULL, NULL);

    sink->m_pQueue = gst_element_factory_make ("queue", NULL);
    sink->m_pConvert = gst_element_factory_make ("videoconvert", NULL);
    sink->m_pSink = gst_element_factory_make ("filesink", NULL);
    sink->m_pEncoder = gst_element_factory_make("x264enc", NULL);
    sink->m_pParser = gst_element_factory_make("matroskamux", NULL);
//    sink->m_pRate = gst_element_factory_make("videorate", NULL);
//    sink->m_pCaps = gst_element_factory_make("capsfilter", "caps_scale");
    sink->removing = FALSE;


    //GstElement* pQueue2 = gst_element_factory_make ("queue", NULL);



    gst_bin_add_many (GST_BIN (m_DataGST.pipeline), (GstElement*)gst_object_ref (sink->m_pQueue),
                      gst_object_ref (sink->m_pConvert),
                      gst_object_ref (sink->m_pEncoder), gst_object_ref (sink->m_pParser), //gst_object_ref (pQueue2),
                      gst_object_ref (sink->m_pSink), NULL);



      gboolean link_ok;
      //link_ok = gst_element_link_filtered (sink->m_pRate, sink->m_pConvert, caps);
        //gst_caps_unref (caps);

//    gst_element_link_many (sink->m_pQueue, sink->m_pRate, sink->m_pCaps, sink->m_pConvert, sink->m_pEncoder, sink->m_pParser, sink->m_pSink, NULL);
    gst_element_link_many (sink->m_pQueue, sink->m_pConvert, sink->m_pEncoder, sink->m_pParser, //pQueue2,
                           sink->m_pSink, NULL);

    /*
    GstCaps *caps;

      caps = gst_caps_new_simple ("video/x-raw",
              //"format", G_TYPE_STRING, "I420",
              //"width", G_TYPE_INT, 1920,
              //"height", G_TYPE_INT, 1080,
              "framerate", GST_TYPE_FRACTION, 60, 1,
              NULL);

              */


    //g_object_set(G_OBJECT(sink->m_pCaps), "caps", caps, NULL);

    g_object_set (sink->m_pSink, "location", strFilename.toStdString().c_str(), NULL);

    g_object_set (sink->m_pEncoder, "bitrate", 10240, NULL);
    g_object_set (sink->m_pEncoder, "byte-stream", true, NULL);
    g_object_set (sink->m_pEncoder, "speed-preset", 1, NULL);
    g_object_set (sink->m_pEncoder, "tune", 4, NULL);


    g_object_set (sink->m_pSink, "sync", TRUE, NULL);
    //g_object_set (sink->m_pSink, "sync", FALSE, NULL);
    //g_object_set (sink->m_pSink, "-e", NULL);



    gst_element_sync_state_with_parent (sink->m_pQueue);
    gst_element_sync_state_with_parent (sink->m_pConvert);
    gst_element_sync_state_with_parent (sink->m_pEncoder);
    gst_element_sync_state_with_parent (sink->m_pParser);
    //gst_element_sync_state_with_parent (pQueue2);
    gst_element_sync_state_with_parent (sink->m_pSink);

    sinkpad = gst_element_get_static_pad (sink->m_pQueue, "sink");
    gst_pad_link (sink->m_pTeePad, sinkpad);
    gst_object_unref (sinkpad);

    g_print ("added\n");

    m_DataGST.m_pSink = sink;
#else



    CustomDataSink *sink = g_new0 (CustomDataSink, 1);
    GstPad *sinkpad;
    GstPadTemplate *templ;

    //gst_element_set_state (m_DataGST.pipeline, GST_STATE_PAUSED);
    templ = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (m_DataGST.tee), "src_%u");

    g_print ("add\n");

    sink->m_pTeePad = gst_element_request_pad (m_DataGST.tee, templ, NULL, NULL);

    if(sink->m_pQueue==NULL)
    {
        sink->m_pQueue = gst_element_factory_make ("queue", NULL);
    }

    //sink->m_pConvert = gst_element_factory_make ("videoconvert", NULL);

    if(sink->m_pConvert==NULL)
    {
        //sink->m_pConvert = gst_element_factory_make ("nvvidconv", NULL);
        sink->m_pConvert = gst_element_factory_make ("videoconvert", NULL);
    }

    sink->m_pSink = gst_element_factory_make ("filesink", NULL);
    //sink->m_pSink = gst_element_factory_make ("fakesink", NULL);

#ifdef __x86_64
    sink->m_pEncoder = gst_element_factory_make("x264enc", NULL);
#else
    //sink->m_pEncoder = gst_element_factory_make("omxh264enc", NULL);
    if(sink->m_pEncoder==NULL)
    {
        sink->m_pEncoder = gst_element_factory_make("nvv4l2h265enc", NULL);
        usleep(100*1000);
    }

#endif
    if(sink->m_pParser==NULL)
    {
        sink->m_pParser = gst_element_factory_make("h265parse", NULL);
    }

    if(sink->m_pMuxer==NULL)
    {
        sink->m_pMuxer = gst_element_factory_make("matroskamux", NULL);
    }

    //sink->m_pRate = gst_element_factory_make("videorate", NULL);
    //sink->m_pCaps = gst_element_factory_make("capsfilter", "caps_scale");
    if(sink->m_pCaps==NULL)
    {
        sink->m_pCaps = gst_element_factory_make("capsfilter", "caps");
    }

    sink->removing = FALSE;



    //gst_bin_add_many (GST_BIN (m_DataGST.pipeline), (GstElement*) gst_object_ref (sink->m_pConvert), (GstElement*)gst_object_ref (sink->m_pQueue),
      //                gst_object_ref (sink->m_pEncoder), gst_object_ref(sink->m_pCaps), gst_object_ref (sink->m_pParser), gst_object_ref (sink->m_pMuxer), gst_object_ref (sink->m_pSink), NULL);

    gst_bin_add_many (GST_BIN (m_DataGST.pipeline), (GstElement*)gst_object_ref (sink->m_pQueue), gst_object_ref (sink->m_pConvert),
                      gst_object_ref (sink->m_pEncoder), gst_object_ref (sink->m_pCaps), gst_object_ref (sink->m_pParser), gst_object_ref (sink->m_pMuxer), gst_object_ref (sink->m_pSink), NULL);



      gboolean link_ok;
      //link_ok = gst_element_link_filtered (sink->m_pRate, sink->m_pConvert, caps);
        //gst_caps_unref (caps);

      //link_ok = gst_element_link_many (sink->m_pConvert, sink->m_pQueue, sink->m_pEncoder, sink->m_pCaps, sink->m_pParser, sink->m_pMuxer, sink->m_pSink, NULL);
      //link_ok = gst_element_link_many (sink->m_pConvert, sink->m_pQueue, sink->m_pEncoder, sink->m_pParser, sink->m_pMuxer, sink->m_pSink, NULL);
      link_ok = gst_element_link_many (sink->m_pQueue, sink->m_pConvert, sink->m_pEncoder, sink->m_pCaps, sink->m_pParser, sink->m_pMuxer, sink->m_pSink, NULL);

    //gst_element_link_many (sink->m_pQueue, sink->m_pRate, sink->m_pCaps, sink->m_pConvert, sink->m_pEncoder, sink->m_pParser, sink->m_pSink, NULL);
    //gst_element_link_many (sink->m_pQueue, sink->m_pConvert, sink->m_pSink, NULL);

    GstCaps *caps;

#ifdef __x86_64
      caps = gst_caps_new_simple ("video/x-raw",
              "format", G_TYPE_STRING, "I420",
              //"width", G_TYPE_INT, 1920,
              //"height", G_TYPE_INT, 1080,
              "framerate", GST_TYPE_FRACTION, 60, 1,
              NULL);
#else
    caps = gst_caps_new_simple ("video/x-h265",
            "stream-format", G_TYPE_STRING, "byte-stream",
            //"width", G_TYPE_INT, 384,
            //"height", G_TYPE_INT, 288,
            //"framerate", GST_TYPE_FRACTION, 30, 1,
            NULL);
#endif



    g_object_set(G_OBJECT(sink->m_pCaps), "caps", caps, NULL);

    g_object_set (sink->m_pSink, "location", strFilename.toStdString().c_str(), NULL);

#ifdef __x86_64
    g_object_set (sink->m_pEncoder, "bitrate", 10000);
#else
    //g_object_set (G_OBJECT(sink->m_pEncoder), "bitrate", 60000000, "peak-bitrate", 150000000);

    g_object_set (sink->m_pEncoder, "bitrate", 10000000, NULL);
    g_object_set (sink->m_pEncoder, "peak-bitrate", 16000000, NULL);
    g_object_set (sink->m_pEncoder, "control-rate", 0, NULL);
    g_object_set (sink->m_pEncoder, "bit-packetization", true, NULL);
    g_object_set (sink->m_pEncoder, "EnableTwopassCBR", true, NULL);
    g_object_set (sink->m_pEncoder, "insert-aud", true, NULL);
    g_object_set (sink->m_pEncoder, "iframeinterval", 10, NULL);
    g_object_set (sink->m_pEncoder, "maxperf-enable", true, NULL);
    g_object_set (sink->m_pEncoder, "profile", 1, NULL);
    g_object_set (sink->m_pEncoder, "vbv-size", 10000000, NULL);
    g_object_set (sink->m_pEncoder, "preset-level", 1, NULL);
    g_object_set (sink->m_pEncoder, "insert-sps-pps", true, NULL);
#endif

    g_object_set (sink->m_pSink, "sync", TRUE, NULL);


    gst_element_sync_state_with_parent (sink->m_pQueue);

    gst_element_sync_state_with_parent (sink->m_pConvert);

    //gst_element_sync_state_with_parent (sink->m_pRate);

    gst_element_sync_state_with_parent (sink->m_pEncoder);
    gst_element_sync_state_with_parent (sink->m_pCaps);

    gst_element_sync_state_with_parent (sink->m_pParser);

    gst_element_sync_state_with_parent (sink->m_pMuxer);

    gst_element_sync_state_with_parent (sink->m_pSink);



    sinkpad = gst_element_get_static_pad (sink->m_pQueue, "sink");



    gst_pad_link (sink->m_pTeePad, sinkpad);

    /*
    gst_element_set_state(m_DataGST.pipeline, GST_STATE_PLAYING);


    GstState checkState;
    gst_element_get_state(m_DataGST.pipeline, &checkState, NULL, 0);
    while(checkState==GST_STATE_PAUSED)
    {
        gst_element_set_state(m_DataGST.pipeline, GST_STATE_PLAYING);
        usleep(100*1000);
        gst_element_get_state(m_DataGST.pipeline, &checkState, NULL, 0);

    }

    GstStateChangeReturn ret = gst_element_set_state (sink->m_pQueue, GST_STATE_PLAYING);

    gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test"));
    */

    gst_object_unref (sinkpad);

    g_print ("added\n");


    m_DataGST.m_pSink = sink;

    /*
    GstPad *pPadSinkSink = NULL;
    pPadSinkSink = gst_element_get_static_pad (sink->m_pSink, "sink");

    ret = gst_element_set_state (sink->m_pSink, GST_STATE_PAUSED);

    gst_pad_send_event (pPadSinkSink, gst_event_new_stream_start ("test"));

    usleep(1000*1000);

    gst_element_set_state(m_DataGST.pipeline, GST_STATE_PLAYING);

    ret = gst_element_set_state (sink->m_pSink, GST_STATE_PLAYING);
    */
#endif
}

void QGstreamerPlayer::SaveEnd()
{
    CustomDataSink* sink =m_DataGST.m_pSink;

    if(sink==NULL)
    {
        return;
    }

    GST_CustomData* pData = (GST_CustomData*)&m_DataGST;

    GstPad *sinkpad;

    sink->removing = TRUE;

    sinkpad = gst_element_get_static_pad (sink->m_pQueue, "sink");

    GstElement* pPipeLine = m_DataGST.pipeline;
    /*


    GstStateChangeReturn ret = gst_element_set_state (pPipeLine, GST_STATE_PAUSED);
    ret = gst_element_set_state (pPipeLine, GST_STATE_READY);
    ret = gst_element_set_state (pPipeLine, GST_STATE_PLAYING);
    return;
    */

    gst_pad_send_event (sinkpad, gst_event_new_eos ());
    //gst_element_send_event (pPipeLine, gst_event_new_eos ());
    gst_pad_unlink (sink->m_pTeePad, sinkpad);

    gst_object_unref (sinkpad);
    //return;

    usleep(100*1000);



#ifdef __x86_64
    //return;

    //gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pRate);
    //gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pCaps);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pConvert);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pSink);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pEncoder);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pParser);

    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pQueue);



    gst_element_set_state (pData->m_pSink->m_pQueue, GST_STATE_NULL);
    //gst_element_set_state (pData->m_pSink->m_pRate, GST_STATE_NULL);
    //gst_element_set_state (pData->m_pSink->m_pCaps, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pConvert, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pSink, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pEncoder, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pParser, GST_STATE_NULL);



    gst_object_unref (pData->m_pSink->m_pQueue);
    //gst_object_unref (pData->m_pSink->m_pRate);
    //gst_object_unref (pData->m_pSink->m_pCaps);
    gst_object_unref (pData->m_pSink->m_pConvert);
    gst_object_unref (pData->m_pSink->m_pSink);
    gst_object_unref (pData->m_pSink->m_pEncoder);
    gst_object_unref (pData->m_pSink->m_pParser);
#else

    //gst_pad_send_event (sinkpad, gst_event_new_eos ());
    //gst_element_send_event (pPipeLine, gst_event_new_eos ());
    //gst_pad_unlink (sink->m_pTeePad, sinkpad);

    //gst_object_unref (sinkpad);
    //return;

    //usleep(1000*1000);

    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pQueue);
    //gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pRate);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pMuxer);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pCaps);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pConvert);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pSink);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pEncoder);
    gst_bin_remove (GST_BIN (pData->pipeline), pData->m_pSink->m_pParser);

    gst_element_set_state (pData->m_pSink->m_pQueue, GST_STATE_NULL);
    //gst_element_set_state (pData->m_pSink->m_pRate, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pCaps, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pMuxer, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pConvert, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pSink, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pEncoder, GST_STATE_NULL);
    gst_element_set_state (pData->m_pSink->m_pParser, GST_STATE_NULL);

    //gst_object_unref (pData->m_pSink->m_pQueue);
    //gst_object_unref (pData->m_pSink->m_pRate);
    //gst_object_unref (pData->m_pSink->m_pCaps);
    //gst_object_unref (pData->m_pSink->m_pMuxer);
    //gst_object_unref (pData->m_pSink->m_pConvert);
    gst_object_unref (pData->m_pSink->m_pSink);
    //gst_object_unref (pData->m_pSink->m_pEncoder);
    //gst_object_unref (pData->m_pSink->m_pParser);
#endif

    gst_element_release_request_pad (pData->tee, pData->m_pSink->m_pTeePad);
    gst_object_unref (pData->m_pSink->m_pTeePad);

    pData->m_pSink = NULL;

    usleep(100*1000);
}

void QGstreamerPlayer::CaptureImage()
{
    m_bCaptureImage = true;
}
