2015-12-17 45 views
10

Stworzyłem oddzielną klasę dla gstreamer do streamowania filmów.
Ta klasa działa w oddzielnym wątku za pomocą funkcji moveToThread().
Używam Qt5.5 do programowania.
Kiedy uruchamiam polecenie startowe w głównym wątku, Qthread startuje, a gstreamer używa g_main_loop_run do streamowania filmów. Działa to absolutnie dobrze. Ale jakoś g_main_loop_run blokuje wątek i kiedy wysyłam sygnał, aby zatrzymać wideo z głównego wątku, nie wykonuje się go w klasie gstreamer.g_main_loop_run blokuje Qthread i nie pozwala zatrzymać wideo

Czy ktoś może mi doradzić, jak rozwiązać ten problem? Albo mogę zastąpić g_main_loop_r un inną komendą lub może być użyty g_main_loop_quit(gloop); innym sposobem.

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. 
} 

// gdzieś w głównym wątku

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

Dlaczego nie wystarczy użyć QMediaPlayer zamiast próbować łączyć Qt i Gtk +? – MrEricSir

+0

Niestety, jest to req projektu, aby używać tylko gstreamer-0.10 nz qt. Lol – samprat

+0

Tak nie możesz użyć gstreamer bez g_main_loop_run? – dtech

Odpowiedz

0

Disclaimer: Nic o GLib/GTK ale co ja szybko google wiedzieć - niektóre inspiracja pochodziła z tego SO zakładać add callback for separate g_main_loop i Docu na https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add

Chodzi o to, że mamy do czynienia z dwiema pętlami zdarzeń - pętlą zdarzeń Qt dla wątku, która jest domyślnie wprowadzona wewnątrz QThread::run(), oraz pętlą GLIB, którą wpisuje się ręcznie w swoim numerze slotStartStream(). Wszystkie sygnały Qt wysyłane z głównego wątku muszą przejść przez dyspozytor Qt - więc musisz dać Qt jakąś szansę na ich przetworzenie, co oznacza, że ​​pętla GLib musi okresowo przekazywać kontrolę do Qt. Pomysł jest taki: zainstaluj wywołanie zwrotne (np. Prosty zegar), który GLib będzie wywoływał okresowo, az tego wywołania zwrotnego wydana zostanie funkcja processEvents() dla Qt, która wykona swoje zadanie.

chciałbym spróbować coś takiego:

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

    [...] 

Teraz nie wiem, czy to rozwiązuje wszystkie problemy (w rzeczywistości jestem pewien, że nie :-)), ale myślę, że przynajmniej zobaczysz, że twoja slotStopStream() zostanie wywołana (w zakresie wątku roboczego) po tych modyfikacjach.

Podsumowując, jest to dość piekielne ustawienie, ale może po prostu działać.

+0

Po modyfikacji mojego kodu, wchodzi on do wnętrza slotStopStream, ale po wykonaniu g_main_loop_quit, wideo nadal się nie zatrzymuje – samprat

2

Nie trzeba polegać na GMainLoop. Rurociąg powinien przebiegać bez problemu, jeśli nie ma g_main_loop_run().

Należy zwrócić uwagę na to, że główna pętla aplikacji Qt musi albo odpytać magistralę potoku za wiadomości, albo użyć gst_bus_set_sync_handler, aby ustawić funkcję zwrotną dla wywoływanej magistrali po nadejściu wiadomości. Później musisz pamiętać, że ta funkcja jest wywoływana z wątku potoku, a nie aplikacji. Emitowanie sygnałów tutaj powinno być jednak dobre.

Jeśli chcesz przejść do wątku, musisz ręcznie utworzyć wątek w aplikacji, który uruchamia GMainLoop. Możliwe również - powyższe wygląda na łatwiejszy i bardziej przejrzysty sposób dla mnie.

0

Nie ma potrzeby korzystania z GMainLoop glib w aplikacji Qt. Qt ma własną wersję GMainLoop (QEventLoop), którą można uznać za metodę exec().

Na przykład, jeśli posiadasz klasę/klasę QGuiApplication, możesz wywołać jej metodę exec(), aby uruchomić główną pętlę zdarzeń. http://doc.qt.io/qt-5/qguiapplication.html#exec

Podobnie, jeśli posiadasz klasę klasy Q/QThread, możesz wywołać jej metodę exec(), aby uruchomić lokalną pętlę zdarzeń. http://doc.qt.io/qt-4.8/qthread.html#exec

Podsumowanie każdy wygadany kod, który potrzebuje pętli zdarzenia wyzwolić swój proces (np g_bus_own_name w GLib trzeba zadzwonić GMainLoop w porządku dla dbus, aby rozpocząć. Jeśli umieścisz to w kodzie Qt, tylko ty trzeba zadzwonić exec() zamiast do czynienia z GMainLoop.

aby długie opowiadanie, jest tylko jedna pętla zdarzenie ale różne implementacje są dokonywane przez inną organizację (np gnome, qT)