Qt6 thread error
-
In my media player project, it contains no QThread and it works well in Qt 6.3.2 on Windows and Android.However, in Qt 6..5.3, it never works.When I try playing a media in debug mode, it shows a critical messagebox.
what it outputs:
failed to get textures for frame; format: 172 textureConverter null
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 0x0x2aec62898c0. Receiver '' (of type 'QLabel') was created in thread 0x0x2aeba8ec7b0", file C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qcoreapplication.cpp, line 541(In fact, the file doesn't exist)Through using QDebug line by line, I found it errors when I use QMediaPlayer::play().Why and how can I solve it?
bool PlayerWidget::play()
{
player->play();
QObject::connect(this,QOverloadQMediaPlayer::Error,QString::of(&PlayerWidget::error_private),[=](QMediaPlayer::Error error,QString string){
if(error != QMediaPlayer::Error::NoError)
{
this->disconnect(this,SIGNAL(error_private(QMediaPlayer::Error,QString)),0,0);
filepath = "";
loop->quit();
}
});
QObject::connect(this,QOverloadQMediaPlayer::MediaStatus::of(&PlayerWidget::statusChanged_private),[=](QMediaPlayer::MediaStatus status){
if(status == QMediaPlayer::BufferedMedia)
{
this->disconnect(this,SIGNAL(statusChanged_private(QMediaPlayer::MediaStatus)),0,0);
loop->quit();
}
});
loop->exec();
if(player->mediaStatus() == QMediaPlayer::BufferedMedia)
return true;
else
return false;
} -
Please format your code with code tags so others can read it and also provide a minimal, compilable exmaple. The error mentions a QLabel but I can't see any label in your code.
-
@Christian-Ehrlicher Actually, the error is mainly about QVideoFrame, not the label or the thread.It seems that the function QVideoFrame::toImage() has some bugs. And I found a similar question in Qt Forum:
https://forum.qt.io/topic/145400/always-get-failed-to-get-textures-for-frame-true-error-for-media?_=1695988710176But I still don't know how to solve it.I have tried QImage(frame.bits(0),frame.width(),frame.height(),QVideoFrameFormat::imageFormatFromPixelFormat(frame.pixelFormat())) but it didn't work.(When I replace the frame image with one from local file, it worked)
-
@Soviet-Ball said in Qt6 thread error:
Actually, the error is mainly about QVideoFrame, not the label
And why is the assertion about QLabel then?
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 0x0x2aec62898c0. Receiver '' (of type 'QLabel') was created in thread 0x0x2aeba8ec7b0",
And again - please properly format your code and provide a minimal, compilable example of your program.
QImage(frame.bits(0),frame.width(),frame.height(),QVideoFrameFormat::imageFormatFromPixelFormat(frame.pixelFormat())) b
Where does this now suddenly comes from? Your code does not has this anywhere...
-
@Christian-Ehrlicher I'm sorry that I may misunderstand your meaning.
Here's the PlayerWidget class, still works well in Qt 6.3.2 and 5.14.2, but can't work in Qt 6.5.3.playerwidget.h:
#ifndef PLAYERWIDGET_H #define PLAYERWIDGET_H #define Multimedia_And_Widgets_Enabled #define Multimedia_Private_Enabled #include <QWidget> #include <QLabel> #include <QFileInfo> #include <QKeyEvent> #ifdef Multimedia_And_Widgets_Enabled #include <QImageWriter> #include <QCamera> #include <QMediaPlayer> #include <QVideoWidget> #include <QMediaMetaData> #include <QVideoFrame> #include <QGraphicsVideoItem> #include <QGraphicsScene> #include <QGraphicsView> #include <QAudioOutput> #include <QMediaRecorder> #include <QAudioDecoder> #if(QT_VERSION>=QT_VERSION_CHECK(6,0,0)) #include <QVideoSink> #include <QVideoFrameFormat> #else #ifdef Multimedia_Private_Enabled #include <private/qvideoframe_p.h> #endif #include <QVideoProbe> #include <QAbstractVideoSurface> #include <QAudioRecorder> #include <QAudioEncoderSettings> #include <QVideoEncoderSettings> #endif #endif class PlayerWidget : public QWidget { Q_OBJECT public: explicit PlayerWidget(QWidget *parent = nullptr); ~PlayerWidget(); enum State { StoppedState, PlayingState, PausedState }; bool eventFilter(QObject *watched, QEvent *event); bool setFilePath(QString p); QString filePath(); QMediaPlayer* mediaPlayer(); bool play(); void pause(); void stop(); void setPosition(int position); int duration(); int volume(); void setVolume(int v); bool hasVideo(); bool hasAudio(); QVideoFrame videoFrame(); QMediaPlayer::MediaStatus mediaStatus(); PlayerWidget::State state(); void lock(); void unlock(); bool isLocked(); signals: void keyRelease(QKeyEvent* event); void error(QMediaPlayer::Error e,QString errorString); void statusChanged(QMediaPlayer::MediaStatus status); void positionChanged(int position); void durationChanged(int duration); void videoFrameChanged(QVideoFrame frame); void hasVideoChanged(bool available); void hasAudioChanged(bool available); void stateChanged(PlayerWidget::State state); void statusChanged_private(QMediaPlayer::MediaStatus status); void error_private(QMediaPlayer::Error e,QString errorString); private: QEventLoop* loop; QMediaPlayer* player; QVideoWidget* videowidget; QLabel* label; QAudioOutput* audiooutput; QVideoFrame frame; QString filepath = ""; bool videoAvailable = false; bool audioAvailable = false; bool enable = true; bool ret; bool widgetmode = false; QPoint startpoint = QPoint(-1,-1); #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) QVideoSink* sink; #else QVideoProbe* probe; #endif }; #endif // PLAYERWIDGET_H
playerwidget.cpp:
#include "playerwidget.h" static bool hasFilePermission(QString filepath,QIODevice::OpenModeFlag device = QIODevice::ReadWrite) { bool check = true; if(filepath != "") { QFile* file = new QFile(filepath); if(!file->exists()) check = false; if(file->open(device)) { file->close(); delete file; if(!check) QFile::remove(filepath); return true; } else { delete file; return false; } } else { return false; } } PlayerWidget::PlayerWidget(QWidget *parent) : QWidget(parent) { qDebug() << "thread of playerwidget.cpp : " << this->thread(); this->setStyleSheet("background-color: black;"); this->setAttribute(Qt::WA_AcceptTouchEvents, true); player = new QMediaPlayer; videowidget = new QVideoWidget(this); audiooutput = new QAudioOutput; label = new QLabel(this); loop = new QEventLoop(this); #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) sink = new QVideoSink; #else probe = new QVideoProbe; #endif label->setStyleSheet("background-color: black;"); label->resize(this->size()); label->setAlignment(Qt::AlignCenter); if(widgetmode) { videowidget->show(); label->hide(); } else { videowidget->hide(); label->show(); } player->setVideoOutput(videowidget); #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) player->setAudioOutput(audiooutput); player->setVideoSink(sink); #else probe->setSource(player); #endif this->installEventFilter(this); #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) connect(player,&QMediaPlayer::errorChanged,[this](){ emit error_private(player->error(),player->errorString()); emit error(player->error(),player->errorString()); }); connect(player,&QMediaPlayer::hasVideoChanged,[this](){ videoAvailable = player->hasVideo(); emit hasVideoChanged(videoAvailable); if(this->hasVideo()) { label->show(); } else { label->clear(); label->hide(); } }); connect(player,&QMediaPlayer::hasAudioChanged,[this](){ audioAvailable = player->hasAudio(); emit hasAudioChanged(audioAvailable); if(this->hasVideo()) { label->show(); } else { label->clear(); label->hide(); } }); connect(player,&QMediaPlayer::playbackStateChanged,[this](){ emit stateChanged(this->state()); }); connect(sink,&QVideoSink::videoFrameChanged,[this](const QVideoFrame &f){ frame = f; emit videoFrameChanged(frame); if(f.isValid() && widgetmode == false && !this->isLocked()) { label->setPixmap(QPixmap::fromImage(f.toImage().scaled(label->size(),Qt::KeepAspectRatio))); } }); #else QObject::connect(player,QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error),[=](){ emit error_private(player->error(),player->errorString()); emit error(player->error(),player->errorString()); }); QObject::connect(player,QOverload<bool>::of(&QMediaPlayer::videoAvailableChanged),[=](bool available){ videoAvailable = available; emit hasVideoChanged(videoAvailable); if(this->hasVideo()) { label->show(); } else { label->clear(); label->hide(); } }); QObject::connect(player,QOverload<bool>::of(&QMediaPlayer::audioAvailableChanged),[=](bool available){ audioAvailable = available; emit hasAudioChanged(audioAvailable); if(this->hasVideo()) { label->show(); } else { label->clear(); label->hide(); } }); QObject::connect(probe,QOverload<const QVideoFrame &>::of(&QVideoProbe::videoFrameProbed),[=](const QVideoFrame &f){ frame = f; emit videoFrameChanged(frame); if(f.isValid() && enable == true && widgetmode == false) { QImage image = qt_imageFromVideoFrame(f); #ifdef Q_OS_WIN image = image.mirrored(); #endif if(!image.isNull()) { image = image.scaled(label->size(),Qt::KeepAspectRatio); label->setPixmap(QPixmap::fromImage(image)); } } }); QObject::connect(player,QOverload<QMediaPlayer::State>::of(&QMediaPlayer::stateChanged),[=](){ emit stateChanged(this->state()); }); #endif connect(player,&QMediaPlayer::mediaStatusChanged,[this](){ emit statusChanged_private(player->mediaStatus()); emit statusChanged(player->mediaStatus()); }); connect(player,&QMediaPlayer::positionChanged,[this](){ emit positionChanged(player->position()); }); connect(player,&QMediaPlayer::durationChanged,[this](){ emit durationChanged(player->duration()); }); } bool PlayerWidget::eventFilter(QObject *watched, QEvent *event) { if(watched == this) { if(event->type() == QEvent::KeyRelease) { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); emit keyRelease(keyEvent); } else if(event->type() == QEvent::Resize) { if(widgetmode) videowidget->resize(this->size()); else label->resize(this->size()); } else if(event->type() == QEvent::Close) { player->stop(); } else if(event->type() == QEvent::Hide) { if(widgetmode) videowidget->hide(); else label->hide(); } else if(event->type() == QEvent::Show) { if(widgetmode) videowidget->show(); else label->show(); } } return QObject::eventFilter(watched,event); } PlayerWidget::~PlayerWidget() { } bool PlayerWidget::setFilePath(QString p) { if(QFileInfo(p).isFile() && hasFilePermission(p,QIODevice::ReadOnly)) { filepath = p; #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) player->setSource(QUrl::fromLocalFile(filepath)); #else player->setMedia(QUrl::fromLocalFile(filepath)); #endif return true; } else { filepath = ""; return false; } } QString PlayerWidget::filePath() { return filepath; } QMediaPlayer* PlayerWidget::mediaPlayer() { return player; } bool PlayerWidget::play() { //QEventLoop* loop = new QEventLoop; player->play(); QObject::connect(this,QOverload<QMediaPlayer::Error,QString>::of(&PlayerWidget::error_private),[=](QMediaPlayer::Error error,QString string){ if(error != QMediaPlayer::Error::NoError) { this->disconnect(this,SIGNAL(error_private(QMediaPlayer::Error,QString)),0,0); filepath = ""; loop->quit(); //loop->exit(0); } }); QObject::connect(this,QOverload<QMediaPlayer::MediaStatus>::of(&PlayerWidget::statusChanged_private),[=](QMediaPlayer::MediaStatus status){ if(status == QMediaPlayer::BufferedMedia) { this->disconnect(this,SIGNAL(statusChanged_private(QMediaPlayer::MediaStatus)),0,0); loop->quit(); //loop->exit(0); } }); this->lock(); loop->exec(); this->unlock(); return bool(player->error()==QMediaPlayer::Error::NoError); } void PlayerWidget::pause() { player->pause(); } void PlayerWidget::stop() { player->stop(); } void PlayerWidget::setPosition(int position) { if(position >= 0) player->setPosition(position); } int PlayerWidget::duration() { return player->duration(); } int PlayerWidget::volume() { #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) return int(player->audioOutput()->volume()*100.0); #else return player->volume(); #endif } void PlayerWidget::setVolume(int v) { #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) player->audioOutput()->setVolume(float(v)/100.0); #else player->setVolume(v); #endif } bool PlayerWidget::hasVideo() { //return player->hasVideo(); return videoAvailable; } bool PlayerWidget::hasAudio() { //return player->hasAudio(); return audioAvailable; } QVideoFrame PlayerWidget::videoFrame() { return frame; } QMediaPlayer::MediaStatus PlayerWidget::mediaStatus() { return player->mediaStatus(); } PlayerWidget::State PlayerWidget::state() { #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) if(player->playbackState() == QMediaPlayer::PlaybackState::PlayingState) { return PlayerWidget::State::PlayingState; } else if(player->playbackState() == QMediaPlayer::PlaybackState::PausedState) { return PlayerWidget::State::PausedState; } else { return PlayerWidget::State::StoppedState; } #else if(player->state() == QMediaPlayer::State::PlayingState) { return PlayerWidget::State::PlayingState; } else if(player->state() == QMediaPlayer::State::PausedState) { return PlayerWidget::State::PausedState; } else { return PlayerWidget::State::StoppedState; } #endif } void PlayerWidget::lock() { enable = false; } void PlayerWidget::unlock() { enable = true; } bool PlayerWidget::isLocked() { if(enable == true) return false; else return true; }
how to use:
PlayerWidget* widget = new PlayerWidget(this); if(widget->setFilePath(QFileDialog::getOpenFileName()) && widget->play()) { return true; } else { return false; }