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