G_main_loop_run блокирует Qthread и не позволяет остановить видео

Я создал отдельный класс для gstreamer для потокового видео.
Этот класс работает в отдельном потоке с помощью moveToThread().
Я использую Qt5.5 для разработки.
Когда я выпускаю startcommand в основном потоке, начинается Qthread, а gstreamer использует g_main_loop_run для потоковой передачи видео. Это работает абсолютно нормально. Но как-то g_main_loop_run блокирует поток, и когда я выдаю сигнал, чтобы остановить видео из основного потока, он не выполняет слот в классе gstreamer.

Может кто-нибудь, пожалуйста, посоветуйте мне, как решить эту проблему? Либо я могу заменить g_main_loop_r un командой someother, либо использовать 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

Нет необходимости полагаться на GMainLoop. Трубопровод должен работать нормально, без g_main_loop_run().

Единственное, что вы должны отметить здесь, это то, что ваш основной цикл приложения Qt должен либо опросить конвейерную шину для сообщений, либо использовать gst_bus_set_sync_handler, чтобы установить функцию обратного вызова для шины, которая будет вызвана при поступлении сообщений. Для более позднего вам нужно знать, что эта функция затем вызывается из потока конвейера, а не из приложения. Испускающие сигналы здесь должны быть хорошими, хотя.

Если вы хотите перейти в поток, вам придется вручную создать поток в приложении, который запускает GMainLoop. Также возможно - выше выглядит как более легкий и более чистый способ для меня, хотя.

Ответ 2

Отказ от ответственности: я ничего не знаю о GLib/GTK, но о том, что я быстро искал в Google - некоторое вдохновение пришло из этого SO post добавить обратный вызов для отдельного 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 будет вызывать периодически, и из этой проблемы обратного вызова a 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 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 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 () на самом деле будет (в рамках рабочего потока) после этих изменений.

В целом, это довольно адская настройка, но она может просто работать.

Ответ 3

Нет необходимости использовать glain GMainLoop внутри приложения Qt. 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)

Ответ 4

Спасибо ОП за поднятие важного вопроса о взаимодействии GLib/Qt, который недостаточно освещен в Интернете. Вот как GMainLoop работает в QThread для меня:

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