g_main_loop_run阻塞了QThread并且不允许停止视频。

12

我已经为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();

1
为什么不直接使用QMediaPlayer,而要试图将Qt和Gtk+拼凑在一起呢? - MrEricSir
你不能在没有 g_main_loop_run 的情况下使用 gstreamer 吗? - dtech
@ddriver,问题在于一旦g_main_loop_run开始执行,它会消耗所有资源,因此即使检查或发送信号,它也无法执行。 - samprat
@samprat - 我没有使用过gstreamer,但通常用户可以自己启动事件循环。 - dtech
@samprat,如果你找到了解决方案,能否在这里发布一下?我也想在QThread中运行glib主循环。 - shang12
显示剩余6条评论
4个回答

2

没有必要依赖于GMainLoop。只要没有g_main_loop_run(),管道应该能够完美运行。

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

如果您想采用线程方式,您需要在应用程序中手动创建一个运行GMainLoop的线程。虽然也可以实现,但前面提到的方法看起来更简单、更清晰。


0
免责声明:我对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之后,视频仍然没有停止。 - samprat

0

在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代码(例如,在glib中,您需要调用GMainLoop才能启动dbus。如果将此放入Qt代码中,则只需调用exec()而不是处理GMainLoop。

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


这不起作用,没有GMainLoop,您无法接受来自gstreamer的信号。 - StereoMatching

0
感谢楼主提出了一个关于GLib/Qt互操作的重要问题,这在互联网上得到的覆盖还不够。以下是我在QThread中使用GMainLoop的工作方式:
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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接