2013-10-03 13 views
5

Piszę aplikację, która osadza wiele instancji libVlc działających jednocześnie, używając Qt. Wygląda na to, że w bibliotece vlc pojawia się błąd, w którym czasami dochodzi do zakleszczenia libvlc_media_player_stop, jeśli wywołano go z wątku GUI Qt. Na jednym z forów videolan, zaakceptowanym rozwiązaniem było wywołanie funkcji stop z innego wątku. Szukam najmniej zaangażowanej i niezbyt brzydkiej metody wywoływania zatrzymania z innego wątku. Spojrzałem na użycie QThreadPool, który jest przeznaczony właśnie dla tego rodzaju sytuacji, ale w moim przypadku nie czyni to rozwiązania ładnym.Jaki jest najprostszy sposób na wystrzelenie i zapomnienie wątku w języku C++/Qt na Linuksie?

Oto fragment mojego kodu:

VlcWidget.h

class VlcWidget : public QWidget 
    { 
     Q_OBJECT 

    private: 

     // State 
     bool _isPlaying; 

     // The streaming source, title and quality data 
     VideoData _videoData; 
     VIDEO_QUALITY _quality; 

     // LibVlc related members 
     libvlc_instance_t *_vlcInstance; 
     libvlc_media_player_t *_vlcMediaPlayer; 
     libvlc_media_t *_vlcMedia; 
     int _vlcTrackID; 
    } 

VlcWidget.c

void VlcWidget::Play() 
    { 
     if(_videoData.Source() != "" && !_isPlaying) 
     { 
      // Create a new media descriptor 
      _vlcMedia = libvlc_media_new_location(
          _vlcInstance, 
          _videoData.Source().toStdString().c_str()); 

      // Tell the user about incorrect URL 
      if(_vlcMedia == NULL) 
      { 
       QMessageBox::information(this, 
             _videoData.Title(), 
             "Unable to open source Url.\nPlease check the source and try again."); 
       return; 
      } 

      libvlc_media_player_set_media(_vlcMediaPlayer, _vlcMedia); 
      libvlc_media_release(_vlcMedia); 
      libvlc_media_player_set_xwindow(_vlcMediaPlayer, parentWidget()->winId()); 
      libvlc_media_player_play(_vlcMediaPlayer); 
      _vlcTrackID = libvlc_audio_get_track(_vlcMediaPlayer); 
      _isPlaying = true; 
     } 
    } 

    void VlcWidget::Stop() 
    { 
     if(_isPlaying) 
     { 
      libvlc_media_player_stop(_vlcMediaPlayer); 
      _vlcTrackID = -1; 
      _isPlaying = false; 
     } 
    } 

Moje rozwiązanie używając QthreadPool wyglądało:

class AsyncVlcPlay : public QRunnable 
    { 
    private: 
     // State 
     bool *_isPlaying; 

     // LibVlc related members 
     libvlc_instance_t *_vlcInstance; 
     libvlc_media_player_t *_vlcMediaPlayer; 
     libvlc_media_t *_vlcMedia; 
     int *_vlcTrackID; 

    public: 
     virtual void run(); 
    } 

I AsyncVlcPl ay :: run() robi dokładnie to, co robi VlcWidget :: Play() z dodaniem prostego blokowania. I potrzebuję podobnej klasy dla VlcWidget :: Stop(). Nie podoba mi się to rozwiązanie, ponieważ nie powinienem potrzebować 2 nowych klas dla tego, co próbuję osiągnąć. A co gorsza, będę musiał przekazać prywatnych członków VlcWidgets do obiektu innej klasy. Jestem pewien, że istnieje bardzo prosty sposób, którego nie jestem świadomy i mam nadzieję, że jeden z was pomoże mi tutaj. Dzięki!

(W rzeczywistości, naprawdę nie trzeba VlcWidget :: play(), aby być w innym wątku, ale chciałbym zachować odtwarzania i zatrzymania symetryczne)

+0

Może [QtConcurrent] (http : //qt-project.org/doc/qt-4.8/qtconcurrentrun.html) jest tym, czego szukasz. – thuga

+0

@thuga - Wielkie dzięki!Właśnie tego szukałem :) – Kulki

+0

Jeśli zamierzasz używać QtConcurrent, sugeruję najpierw przeczytać: http://comments.gmane.org/gmane.comp.lib.qt.devel/7942 – TheDarkKnight

Odpowiedz

0

tutaj jest mój (część) kod do sprzęgania SWI-Prolog podświetlanie składni do Qt

// start highlighting, using SWI-Prolog syntax analyzer to collect structured data 
// 
void pqSource::startHighliter() { 
    auto f = [](QString file, pqSyntaxData* psd) { 
     SwiPrologEngine::in_thread _it; 
     try { 
      // ?? PlCall("consult", V(A(file))); 
      qDebug() << "running syntax color on" << file << "thread" << CT; 
      int rc = PlCall("syncol", PlTermv(A(file), PlTerm(psd))); 
      qDebug() << "syncol" << rc; 
     } 
     catch(PlException e) { 
      qDebug() << t2w(e); 
     } 
    }; 

    // collect structure asyncronously 
    auto w = new QFutureWatcher<void>; 
    connect(w, SIGNAL(finished()), this, SLOT(runHighliter())); 

    // since could be a slow task, place a visual hint to what's going on... 
    CenterWidgets((sd = new pqSyntaxData)->pgb = new QProgressBar(this)); 
    QTextCursor c = textCursor(); 
    c.movePosition(c.End); 
    sd->pgb->setMaximum(c.position()); 
    connect(sd, SIGNAL(onProgress(int)), sd->pgb, SLOT(setValue(int))); 
    sd->pgb->show(); 

    // run the Prolog snippet in background (hl pointer) 
    w->setFuture(QtConcurrent::run(f, file, sd)); 
} 

myślę, że może być zainteresowany wykorzystaniem lambda ...

1

bym rozwiązania tego problemu z QThread. Jego nazwa jest w rzeczywistości myląca, ponieważ nie jest to w rzeczywistości wątek, ale kontroler wątków i bardzo łatwy w użyciu.

Każda klasa odziedziczona po QObject może zostać przeniesiona do wątku, a komunikacja między wątkami może zostać wykonana za pomocą mechanizmu sygnału/szczeliny. Dlatego możesz zrobić coś takiego: -

class VlcObject : public QObject 
{ 
    Q_OBJECT 

    public slots: 
     void Run(); 

    private slots; 
     void StopVlc(); 
}; 

Ta klasa może zawierać wszystkie obiekty/instancje Vlc. Następnie należy utworzyć obiekt kontrolera wątek i przenieść instancji VlcObject do nowego wątku: -

QThread* pThread = new QThread(this); // where this is a parent, running on the main thread 
VlcObject* pVlcObj = new VlcObject; 

pVlcObj->moveToThread(pThread); 

// Note, this is Qt 5 connect style - Qt 4 connections also work 
connect(pThread, &QThread::started, pVlcOj, &VlcObject::Run(); 

// Start the thread running 
pThread->start(); 

Zakładając QVlcWidget jest klasa GUI z przyciskiem o nazwie pStopVlc, następnie zadzwonić zatrzymać na drugim wątku VlcObject podłączając przycisku do funkcji StopVlc w VlcObject za: -

connect(pStopVlc, &QPushButton::released, pVlcObj, &VlcObject::StopVlc); 

Ewentualnie mogłeś StopVlc wywoływana, gdy wątek zostanie zamknięty i jest możliwe, że QThread oczyścić się, gdy się zatrzyma: -

connect(pThread, &QThread::finished, pThread, &Qthread::deleteLater); 
+1

Uwielbiam tę część, w której tworzysz wątek na stercie i zapominasz o tym po podłączeniu do slotu deleteLater! Nie miałem pojęcia, że ​​możesz to zrobić (mam już tylko tydzień w Qt). – Kulki

+0

A czy -> moveToThread oznacza, że ​​wszystko, co pVlcObj zrobi w przyszłości, znajdzie się w nowym wątku? To prawdopodobnie nie jest to, czego chcę. – Kulki

+0

@Kulki można dodawać sygnały do ​​pVlcObj i podłączać je tak, aby uruchamiały slot w innym wątku. –

Powiązane problemy