我正在编写一个embedded了多个使用Qt同时运行的libVlc实例的应用程序。 似乎在vlc库中有一个错误,如果从Qt的GUI线程调用libvlc_media_player_stop死锁,有时会出现死锁。 在其中一个videolan论坛上,接受的解决scheme是从另一个线程调用stop函数。 我正在寻找最不参与和不太丑陋的方法来调用从另一个线程停止。 我看着使用QThreadPool,这正是为了这种情况,但在我的具体情况下,它并没有使解决scheme漂亮。
这是我的一段代码:
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; } }
我使用QthreadPool的解决scheme如下所示:
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(); }
和AsyncVlcPlay :: run()完全一样,VlcWidget :: Play()通过简单的locking添加到它。 而且我还需要一个类似的VlcWidget :: Stop()。 我不喜欢这个解决scheme,因为我不需要2个新的类来实现我想要的。 而更糟糕的是,我必须将VlcWidgets私有成员传递给另一个类的对象。 我敢肯定,有一个我不知道的极其简单的方法,希望你们中的一个能够帮助我。 谢谢!
(事实上,我并不需要VlcWidget :: Play()在另一个线程,但我想保持播放和停止对称)
这里是我的(部分)用于将SWI-Prolog语法高亮显示到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)); }
我想你可能会对lambda的使用感兴趣。
我会用QThread解决这个问题。 它的名字实际上是误导性的,因为它实际上不是一个线程,而是一个线程控制器,非常易于使用。
任何从QObject继承的类都可以移动到一个线程中,线程之间的通信可以通过信号/插槽机制来完成。 因此,你可以做这样的事情:
class VlcObject : public QObject { Q_OBJECT public slots: void Run(); private slots; void StopVlc(); };
这个类可以包含所有的Vlc对象/实例。 然后创建一个线程控制器对象,并将VlcObject实例移动到新线程中: –
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();
假设QVlcWidget是一个带有名为pStopVlc的按钮的GUI类,然后通过将按钮连接到VlcObject的StopVlc函数来调用另一个线程的VlcObject上的stop: –
connect(pStopVlc, &QPushButton::released, pVlcObj, &VlcObject::StopVlc);
或者,当线程退出时,可以调用StopVlc,而QThread在停止时可以自行清理: –
connect(pThread, &QThread::finished, pThread, &Qthread::deleteLater);