12
votes

I have created a separate class for gstreamer to stream videos.
This class runs on separate thread by using moveToThread().
I am using Qt5.5 for development.
When I issue startcommand on main thread , Qthread starts and gstreamer uses g_main_loop_run to stream videos. This works absolutely fine. But somehow g_main_loop_run blocks the thread and when I issue signal to stop video from main thread , it doesnt execute slot in gstreamer class.

Can someone please advice me how to resolve that issue? Either I can replace g_main_loop_run with someother command or may be use g_main_loop_quit( gloop ); in another way.

void StreamingVideo::slotStartStream() // this slot called on start of thread from main thread
{

    if( !isElementsLinked() )
    {
       qDebug() << " we are emitting in dummy server ";
        //emit sigFailed( "elementsFailed" ); // WILL CONNECT IT WITH MAIN GUI ONXCE CODE IS FINISHED
        return;
    }

    gst_bus_add_watch( bus, busCall, gloop );
    gst_object_unref( bus );

    //proper adding to pipe
    gst_bin_add_many( GST_BIN( pipeline ), source, capsFilter, conv, videoRate, capsFilterRate,
                      clockDisplay, videoEnc, udpSink, NULL
                     );

    //proper linking:
    gst_element_link_many( source, capsFilter, conv, videoRate, capsFilterRate, clockDisplay, videoEnc, udpSink, NULL );

    g_print("Linked all the Elements together\n");
    gst_element_set_state( pipeline, GST_STATE_PLAYING );
    // Iterate
    g_print ("Running...\n");
    emit sigStartStream(); // signal to main thread to issue success command . works fine
    g_main_loop_run( gloop );
    g_print ("Returned, stopping playback\n");
    //gst_element_set_state (pipeline, GST_STATE_NULL);
    if( g_main_loop_is_running( gloop ) )
    {
        qDebug() << " in g_main_loop_is_runnung  emiting signal ";
        emit sigStartStream();
    }
    if( !g_main_loop_is_running( gloop) )
    {
        qDebug() << "in not gmain running thread id";
        qDebug() << QThread::currentThreadId();
    }

}



void StreamingVideo::slotStopStream() // THIS SLOT IS NOT CALLED WHEN VIDEO RUNNING
{
    qDebug() << " we are planning to stop streaming  stramingVideo::slotStopStream ";
    g_print ("Returned, stopping playback\n");
    g_main_loop_quit( gloop );
    gst_element_set_state (pipeline, GST_STATE_NULL);
   // g_main_loop_quit( gloop );
    releaseMemory();
    emit sigStopStream(); // signal to main thread to issue message saying video has stopped.
}

// somewhere in main thread

 threadStreaming = new QThread();
 streamVideo    = new StreamingVideo( "127.0.0.1"); // we will automate this ip address later on

        streamVideo->moveToThread( threadStreaming );

        connect( threadStreaming, SIGNAL( started() ),        streamVideo,     SLOT( slotStartStream() ) );
        connect( streamVideo,     SIGNAL( sigStopStream() ),  threadStreaming, SLOT( quit() ) );
        connect( streamVideo,     SIGNAL( sigStopStream() ),  streamVideo,     SLOT(deleteLater() ) );
        connect( threadStreaming, SIGNAL( finished() ),       threadStreaming, SLOT(deleteLater() ) );

        connect( streamVideo,     SIGNAL( sigStartStream() ), this, SLOT( slotTrueStreamRun()  ) );
        connect( streamVideo,     SIGNAL( sigStopStream() ),  this, SLOT( slotFalseStreamRun() ) );

        connect( this,            SIGNAL( sigMopsCamStopCmd() ), streamVideo, SLOT(slotStopStream() ) );
        threadStreaming->start();
4
Why not just use QMediaPlayer instead of trying to cobble together Qt and Gtk+?MrEricSir
Unfortunately, it's req of project to use only gstreamer-0.10 n with qt. Lolsamprat
So you can't use gstreamer without g_main_loop_run?dtech
In a proper design, you should not have to kill the thread, the event loop, whatever will be, will have some way of terminating, even if you don't have async/queued communication between threads. You can for example use an atomic integer, and check that on every loop iteration, which you can set from the main thread so the streaming thread exits its loop.dtech
@ddriver, the problem is once g_main_loop _run start executing it consumes all resources so even if check or send signal it wont able to executesamprat

4 Answers

2
votes

There is no need to rely on aGMainLoop. The pipeline should run just fine without a g_main_loop_run().

The one thing you have to note here is that your main Qt application loop will either have to poll the pipeline's bus for messages OR use gst_bus_set_sync_handler to set a callback function for the bus to be called as messages arrive. For the later you have to be aware that this function is then called from the pipeline's thread and not the application's one. Emitting signals here should be fine though.

If you want to go the thread way you would have to manually create a thread in your application which runs the GMainLoop. Also possible - the above looks like the easier and cleaner way to me though.

0
votes

Disclaimer: I know nothing about GLib/GTK but what I quickly googled -- some inspiration came from this SO post add callback for separate g_main_loop and the docu at https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add

The thing is that you are dealing with two event loops -- the Qt event loop for the thread, which is implicitly entered inside QThread::run (), and the GLib loop, which you enter manually inside your slotStartStream (). All Qt signals you send from the main thread have to go through the Qt dispatcher -- so you have to give Qt some chance to process them, and that means that the GLib loop needs to hand over control to Qt periodically. So the idea is: install a callback (e.g. a simple timer) that GLib will call periodically, and from that callback issue a processEvents () function for Qt to do its job.

I would try something like that:

gboolean myCallback (gpointer user_data)
{
  // The Qt thread is passed as a parameter during callback installation

  QThread* workerThread = reinterpret_cast<QThread*> (user_data);

  // Access the Qt thread's event dispatcher...

  QAbstractEventDispatcher* disp = workerThread->eventDispatcher ();

  // ...and let it do its work

  disp->processEvents (QEventLoop::AllEvents);

  return G_SOURCE_CONTINUE;
}

void StreamingVideo::slotStartStream ()
{
  [...]

  g_print ("Running...\n");

  // Install callback to intertwine Qt's thread-local eventloop with our GLib loop
  g_timeout_add (50, myCallback, QThread::currentThread ());

  emit sigStartStream(); // signal to main thread to issue success command . works fine

  g_main_loop_run( gloop );
  g_print ("Returned, stopping playback\n");

  [...]

Now, I don't know if that solves all your problems (in fact I'm pretty sure it doesn't :-)), but I think you will at least see that your slotStopStream () will in fact be called (in the scope of the worker thread) after those modifications.

All in all, it's a rather hellish setup, but it might just work.

0
votes

There is no need on using glib's GMainLoop inside a Qt application. Qt has it's own version of GMainLoop (QEventLoop) which you can just think of as the exec() method.

For example, if you have a QGuiApplication class/derived class, you can call its exec() method to start its main event loop. http://doc.qt.io/qt-5/qguiapplication.html#exec

Similarly, if you have a QThread class/derived class, you can call its exec() method to start its local event loop. http://doc.qt.io/qt-4.8/qthread.html#exec

Summary, any glib code that needs an event loop to trigger its process (e.g. g_bus_own_name, in glib you need to call GMainLoop in order for the dbus to start. If you put this in a Qt code, you only need to call exec() instead of dealing with GMainLoop.

To make long story short, there is only one event loop but different implementations are made by different organization (e.g. gnome, qt)

0
votes

Thanks to the OP for raising an important question of GLib/Qt interop, which is insufficiently covered on the Internet. This is how GMainLoop works in a QThread for me:

QObject context;
QThread thread;
void* arvMainLoop;

...

context.moveToThread(&thread);
QObject::connect(&thread, &QThread::started, &context, [&]()
{
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
    arvMainLoop = reinterpret_cast<void*>(loop);

    GMainContext* context = g_main_loop_get_context(loop);

    // TODO Maybe make interruption checks less frequent here.
    while (!thread.isInterruptionRequested())
        g_main_context_iteration(context, FALSE);

    g_main_loop_quit(loop);
});

thread.start();

...

thread.requestInterruption();
thread.quit();
thread.wait();

GMainLoop* loop = reinterpret_cast<GMainLoop*>(arvMainLoop);
g_main_loop_unref(loop);