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

Posted

技术标签:

【中文标题】g_main_loop_run 阻塞 Qthread 并且不允许停止视频【英文标题】:g_main_loop_run blocks the Qthread and does not allow to stop video 【发布时间】:2016-03-23 08:48:53 【问题描述】:

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

有人可以告诉我如何解决这个问题吗?我可以用其他命令替换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 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.

// 主线程某处

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

【问题讨论】:

为什么不直接使用 QMediaPlayer 而不是尝试将 Qt 和 Gtk+ 拼凑在一起? 不幸的是,项目要求仅将 gstreamer-0.10 n 与 qt 一起使用。大声笑 所以你不能在没有 g_main_loop_run 的情况下使用 gstreamer? 在一个适当的设计中,你不应该杀死线程,事件循环,不管是什么,都会有某种终止方式,即使你没有线程之间的异步/排队通信.例如,您可以使用原子整数,并在每次循环迭代时检查它,您可以从主线程设置,以便流线程退出其循环。 @ddriver,问题是一旦 g_main_loop _run 开始执行它会消耗所有资源,所以即使检查或发送信号它也无法执行 【参考方案1】:

感谢 OP 提出了 GLib/Qt interop 的一个重要问题,该问题在 Internet 上没有得到充分的介绍。这就是 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);

【讨论】:

【参考方案2】:

没有必要在 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)做出了不同的实现

【讨论】:

这个不行,没有GMainLoop,你不能接受来自gstreamer的信号【参考方案3】:

没有必要依赖aGMainLoop。没有g_main_loop_run(),管道应该可以正常运行。

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

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

【讨论】:

【参考方案4】:

免责声明:我对 GLib/GTK 一无所知,但我很快用谷歌搜索了一些东西——一些灵感来自这篇 SO 帖子 add callback for separate g_main_loop 和 https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add 的文档

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

我会尝试这样的:

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

  [...]

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

总而言之,这是一个相当糟糕的设置,但它可能会奏效。

【讨论】:

其实,修改我的代码后,它进入了 slotStopStream 但执行 g_main_loop_quit 后,视频仍然没有停止

以上是关于g_main_loop_run 阻塞 Qthread 并且不允许停止视频的主要内容,如果未能解决你的问题,请参考以下文章

在 PyQt5 中将参数传递给 QThread 子类时出错

阻塞赋值和非阻塞赋值有何区别

IO阻塞非阻塞同步异步同步阻塞同步非阻塞异步阻塞异步非阻塞

同步阻塞同步非阻塞异步阻塞异步非阻塞--简明介绍

verilog 阻塞和非阻塞啥区别啊?

并行,并发,串行,同步,异步,阻塞,非阻塞,同步阻塞,同步非阻塞,异步阻塞,异步非阻塞