Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to interrupt GStreamer pipeline from Qt?
QtWS25 Last Chance

How to interrupt GStreamer pipeline from Qt?

Scheduled Pinned Locked Moved Solved General and Desktop
gstreamer1.0
10 Posts 3 Posters 2.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    hyperlight
    wrote on last edited by
    #1

    I have a very simple Qt GUI program, it has a push button, when the button is clicked, its slot function will call run_pipeline() which setup and run a GStreamer pipeline (v4l2src -> capsfilter -> xvimagesink). I want to add a Stop button that when clicked will terminate / interrupt the running pipeline. How can I achieve such task?

    I tried to use add signal handler to the pipeline g_unix_signal_add(SIGINT, (GSourceFunc)intr_handler, pipeline);, which send a message that the bus callback function will catch and terminate the pipeline by closing the loop. Below are the code related to GStreamer.

    static guint signal_watch_intr_id;
    
    static gboolean intr_handler(gpointer user_data)
    {
        GstElement *pipeline = (GstElement *)user_data;
    
        /* post an application specific message */
        gst_print("\nPosting UserInterrupt message to bus.\n");
        gst_element_post_message(GST_ELEMENT(pipeline),
                                 gst_message_new_application(GST_OBJECT(pipeline),
                                                             gst_structure_new("UserInterrupt", "message", G_TYPE_STRING, "Pipeline interrupted", NULL)));
    
        /* remove signal handler */
        signal_watch_intr_id = 0;
        return G_SOURCE_REMOVE;
    }
    
    static gboolean bus_call(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_printerr("ERROR from element %s: %s\n",
                       GST_OBJECT_NAME(msg->src), error->message);
            if (debug)
                g_printerr("Error details: %s\n", debug);
            g_free(debug);
            g_error_free(error);
            g_main_loop_quit(loop);
            break;
        }
        case GST_MESSAGE_APPLICATION:
        {
            const GstStructure *s;
            s = gst_message_get_structure(msg);
            if (gst_structure_has_name(s, "UserInterrupt"))
            {
                /* this application message is posted when we caught an interrupt and we need to stop the pipeline. */
                g_print("UserInterrupt message posted, stopping pipeline...\n");
                g_main_loop_quit(loop);
            }
            break;
        }
        default:
            break;
        }
        return TRUE;
    }
    
    void run_pipeline()
    {
        GMainLoop *loop = NULL;
        GstElement *pipeline;
        GstBus *bus;
        guint bus_watch_id;
    
        GstElement *source = NULL,
                   *caps_filter = NULL,
                   *sink = NULL;
    
        GstCaps *caps = NULL;
    
        /* Standard GStreamer initialization */
        gst_init(NULL, NULL);
        loop = g_main_loop_new(NULL, FALSE);
    
        pipeline = gst_pipeline_new("gstcam");
        source = gst_element_factory_make("v4l2src", "camera");
        caps_filter = gst_element_factory_make("capsfilter", "cap_filter");
        sink = gst_element_factory_make("xvimagesink", "sink");
    
        if(!pipeline || !source || !caps_filter || !sink)
        {
            g_print("Fail to create elements\n");
            return;
        }
    
        g_object_set(G_OBJECT(source), "device", "/dev/video0", NULL);
        char buffer[100];
        snprintf(buffer, 100,
                 "video/x-raw, width=%d, height=%d, framerate=30/1",
                 640,
                 480);
        caps = gst_caps_from_string(buffer);
        g_object_set(G_OBJECT(caps_filter), "caps", caps, NULL);
        gst_caps_unref(caps);
    
        gst_bin_add_many(GST_BIN(pipeline),
                         source, caps_filter, sink,
                         NULL);
    
        if(!gst_element_link_many(source, sink, NULL))
        {
            g_print("Fail to link elements\n");
            return;
        }
    
        /*Attach signal hanlder to pipeline*/
        signal_watch_intr_id = g_unix_signal_add(SIGINT, (GSourceFunc)intr_handler, pipeline);
    
        /* Add a bus message handler */
        bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
        bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
        gst_object_unref(bus);
    
        /* Set the pipeline to "playing" state */
        g_print("Now playing pipeline\n");
        gst_element_set_state(pipeline, GST_STATE_PLAYING);
    
        /* Wait till pipeline encounters an error or EOS */
        g_print("Running...\n");
        GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "app-playing");
        g_main_loop_run(loop);
    
        /* Out of the main loop, clean up nicely */
        g_print("Returned, stopping playback\n");
        gst_element_set_state(pipeline, GST_STATE_NULL);
        g_print("Deleting pipeline\n");
    
        if (signal_watch_intr_id > 0)
        {
            g_source_remove(signal_watch_intr_id);
        }
    
        g_source_remove(bus_watch_id);
        g_main_loop_unref(loop);
        gst_object_unref(GST_OBJECT(pipeline));
    }
    

    Signal-slot related functions

    void MainWindow::onStartBtnClicked()
    {
        std::cout << "Pipeline started!" << std::endl;
        run_pipeline();
    }
    
    void MainWindow::makeConnections()
    {
        connect(ui->startBtn, &QPushButton::clicked, this, &MainWindow::onStartBtnClicked);
    }
    

    When I click the button, the pipeline start running, but Ctrl+C does not interrupt it.

    How to I interrupt the running pipeline from Qt (by clicking a button)?

    1 Reply Last reply
    0
    • JoeCFDJ Online
      JoeCFDJ Online
      JoeCFD
      wrote on last edited by JoeCFD
      #3

      You run the pipeline in a qprocess. You can stop it by sending Ctrl
      +C in this way kill( m_process->processId(), SIGINT );

      H 1 Reply Last reply
      1
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #2

        Hi and welcome to devnet,

        Aren't you blocking Qt's event loop with your current implementation ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        H 1 Reply Last reply
        0
        • JoeCFDJ Online
          JoeCFDJ Online
          JoeCFD
          wrote on last edited by JoeCFD
          #3

          You run the pipeline in a qprocess. You can stop it by sending Ctrl
          +C in this way kill( m_process->processId(), SIGINT );

          H 1 Reply Last reply
          1
          • SGaistS SGaist

            Hi and welcome to devnet,

            Aren't you blocking Qt's event loop with your current implementation ?

            H Offline
            H Offline
            hyperlight
            wrote on last edited by
            #4

            @SGaist

            Thank you for the warm welcome.

            Please correct me if I'm wrong. If Qt's event loop is blocked, it means when I click the start button when the pipeline is running, the associated slot function will not be executed right?

            When I clicked the start button when the pipeline is running, onStartBtnClicked() is still executed, the newly created pipeline just complain that the camera is busy (since it's being used by the currently running pipeline) and stop immediately.

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #5

              Good, then what you should do is encapsulate your GStreamer logic in a class so that you can keep a reference to everything that is created. You can then add a stop function that you will call from your stop button clicked handler.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              H 1 Reply Last reply
              1
              • JoeCFDJ JoeCFD

                You run the pipeline in a qprocess. You can stop it by sending Ctrl
                +C in this way kill( m_process->processId(), SIGINT );

                H Offline
                H Offline
                hyperlight
                wrote on last edited by
                #6

                @JoeCFD Thank for the suggestion, I tried to use the regular getpid() instead and it worked, the idea is to get pid of run_pipeline(), give widget access to that pid and use kill() function to sent SIGINT to the process running run_pipeline(). I'm not sure how to find the pid of the qprocess runing the run_pipeline(). I'm mixing C++ and C in this case, so run_pipeline() is in C land and is compiled using a C compiler (gcc), the linker is g++ though.

                JoeCFDJ 1 Reply Last reply
                0
                • H hyperlight

                  @JoeCFD Thank for the suggestion, I tried to use the regular getpid() instead and it worked, the idea is to get pid of run_pipeline(), give widget access to that pid and use kill() function to sent SIGINT to the process running run_pipeline(). I'm not sure how to find the pid of the qprocess runing the run_pipeline(). I'm mixing C++ and C in this case, so run_pipeline() is in C land and is compiled using a C compiler (gcc), the linker is g++ though.

                  JoeCFDJ Online
                  JoeCFDJ Online
                  JoeCFD
                  wrote on last edited by
                  #7

                  @hyperlight auto pid = m_process->processId(); It is in my post.

                  H 1 Reply Last reply
                  0
                  • SGaistS SGaist

                    Good, then what you should do is encapsulate your GStreamer logic in a class so that you can keep a reference to everything that is created. You can then add a stop function that you will call from your stop button clicked handler.

                    H Offline
                    H Offline
                    hyperlight
                    wrote on last edited by
                    #8

                    @SGaist thank you for the suggestion!

                    1 Reply Last reply
                    0
                    • JoeCFDJ JoeCFD

                      @hyperlight auto pid = m_process->processId(); It is in my post.

                      H Offline
                      H Offline
                      hyperlight
                      wrote on last edited by
                      #9

                      @JoeCFD no where in my code did I create a QProcess explicitly, so I'm not sure where m_process is defined?

                      JoeCFDJ 1 Reply Last reply
                      0
                      • H hyperlight

                        @JoeCFD no where in my code did I create a QProcess explicitly, so I'm not sure where m_process is defined?

                        JoeCFDJ Online
                        JoeCFDJ Online
                        JoeCFD
                        wrote on last edited by JoeCFD
                        #10

                        @hyperlight This is a member pointer defined in the class header in which it is used.
                        Learn some hungary notation. Some people hate it. I use it, but not strictly.
                        https://en.wikipedia.org/wiki/Hungarian_notation

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved