g_main_loop_run阻塞Qthread并且不允许停止视频

g_main_loop_run blocks the Qthread and does not allow to stop video

本文关键字:不允许 视频 Qthread main loop run 阻塞      更新时间:2023-10-16

我为gstreamer创建了一个单独的类来流式传输视频
这个类通过使用moveToThread()在单独的线程上运行
我正在使用Qt5.5进行开发
当我在主线程上发出startcommand时,Qthread启动,gstreamer使用g_main_loop_run来流式传输视频。这绝对行得通。但不知何故,g_main_loop_run阻塞了线程,当我从主线程发出停止视频的信号时,它不会执行gstreamer类中的slot。

有人能告诉我如何解决这个问题吗?我可以用其他命令替换g_main_loop_run,也可以使用g_main_loop_quit( gloop );以另一种方式。

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 togethern");
    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 playbackn");
    //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 playbackn");
    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.
}

//主线程中的某个位置

 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();

不需要依赖GMainLoop。管道在没有g_main_loop_run()的情况下应该运行得很好。

这里需要注意的一点是,您的主Qt应用程序循环必须轮询管道的总线以获取消息,或者使用gst_bus_set_sync_handler为总线设置回调函数,以便在消息到达时调用该总线。稍后,您必须注意,该函数是从管道线程调用的,而不是从应用程序线程调用的。不过在这里发射信号应该没问题。

如果你想采用线程方式,你必须在你的应用程序中手动创建一个运行GMainLoop的线程。也有可能——不过,对我来说,以上看起来更简单、更干净。

免责声明:我对GLib/GTK一无所知,但我很快就在谷歌上搜索到了——一些灵感来自于这篇SO文章,为单独的g_main_loop和位于https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-超时添加

问题是,您要处理两个事件循环——线程的Qt事件循环,它在QThread::run ()中隐式输入,以及GLib循环,它是在slotStartStream ()中手动输入的。你从主线程发送的所有Qt信号都必须经过Qt调度器——所以你必须给Qt一些处理它们的机会,这意味着GLib循环需要定期将控制权移交给Qt。因此,我们的想法是:安装一个GLib将定期调用的回调(例如,一个简单的计时器),并从该回调中为Qt发出一个processEvents ()函数来完成它的工作。

我会试试这样的东西:

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 playbackn");
  [...]

现在,我不知道这是否解决了您的所有问题(事实上,我很确定它不会:-),但我认为您至少会看到,在这些修改之后,您的slotStopStream ()实际上会被调用(在工作线程的范围内)。

总而言之,这是一个相当地狱般的设置,但它可能只是起作用。

在Qt应用程序中不需要使用glib的GMainLoop。Qt有自己版本的GMainLoop(QEventLoop),您可以将其视为exec()方法。

例如,如果你有一个QGuiApplication类/派生类,你可以调用它的exec()方法来启动它的主事件循环。http://doc.qt.io/qt-5/qguiapplication.html#exec

类似地,如果你有一个QThread类/派生类,你可以调用它的exec()方法来启动它的本地事件循环。http://doc.qt.io/qt-4.8/qthread.html#exec

总之,任何需要事件循环来触发其进程的glib代码(例如g_bus_own_name,在glib中,你需要调用GMainLoop才能启动dbus。如果你把它放在Qt代码中,你只需要调用exec(),而不需要处理GMainLoop。

长话短说,只有一个事件循环,但不同的组织(例如gnome、qt)会进行不同的实现。

感谢OP提出了一个关于GLib/Qt互操作的重要问题,该问题在互联网上没有得到充分的报道。这就是GMainLoopQThread中的工作方式:

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);