5

我正在编写一个嵌入多个使用 Qt 同时运行的 libVlc 实例的应用程序。vlc 库中似乎存在一个错误,如果从 Qt 的 GUI 线程调用,有时 libvlc_media_player_stop 会死锁。在其中一个 videolan 论坛上,公认的解决方案是从另一个线程调用停止函数。我正在寻找最少参与且不太难看的方法来从不同的线程调用停止。我研究了使用 QThreadPool ,它完全适用于这种情况,但在我的特殊情况下,它并没有使解决方案变得漂亮。

这是我的一段代码:

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 的解决方案如下所示:

    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() 所做的完全一样,只是添加了简单的锁定。而且我还需要一个用于 VlcWidget::Stop() 的类似类。我不喜欢这个解决方案,因为我不应该真的需要 2 个新课程来实现我想要实现的目标。更糟糕的是,我必须将 VlcWidgets 私有成员传递给另一个类的对象。我很确定有一种我不知道的非常简单的方法,希望你们中的一个可以在这里帮助我。谢谢!

(事实上​​,我真的不需要 VlcWidget::Play() 在另一个线程上,但我想保持 Play 和 Stop 对称)

4

2 回答 2

1

我会用 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 上的停止:-

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

或者,您可以在线程退出时调用 StopVlc,并且 QThread 可以在它停止时自行清理:-

connect(pThread, &QThread::finished, pThread, &Qthread::deleteLater);
于 2013-10-04T09:44:10.340 回答
0

这是我的(部分)代码,用于将 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 的使用感兴趣...

于 2013-10-04T11:17:43.987 回答