QtConcurrent::mapped with a member function with QEventLoop
-
Hi,
[OS Windows, Qt6.6.1, IDE. Qt Creator. ]
I am writing some code which finds all media files (images & videos) in a folder and than reads and returns all meta data. Meta data is than used for numerous purposes, one being to be able to sort files by creation date (= when the media was shot, not when the file was dumped on hard drive of the PC or external HD) ascending or descending.I use QtConcurrent:mapped for multithreading as there is a large number of media files.
My code (the relevant part for this issue starts with declaration of
QMediaPlayer player;
below://fileListIndex is in place. auto sequence = QVector<int>::fromList(fileListIndex); auto future = QtConcurrent::mapped(sequence, std::bind(&MappedProcessor::getMetaData, this, std::placeholders::_1)); m_futureWatcherLoadMetaData->setFuture(future);
MetaDataInfo MappedProcessor::getMetaData(int index) { MetaDataInfo info; info.fileIndex = index; QString file = model->loadDirModel.files.at(index); QFileInfo f(file); info.fileSize = f.size(); info.mediaFileType = (isImageFile(file)? image : video); QMediaPlayer player; player.setSource(QUrl::fromLocalFile(file)); QTimer timer; timer.setSingleShot(true); QEventLoop loop; connect(&player, &QMediaPlayer::mediaStatusChanged, &loop,&QEventLoop::quit); connect( &timer, &QTimer::timeout, &loop, &QEventLoop::quit ); timer.start(10000); loop.exec(); if(timer.isActive()) { info.metadata = player.metaData(); info.metaDataIsValid = true; if(info.mediaFileType == video) { info.dt = info.metadata.value(QMediaMetaData::Date).toDateTime(); info.duration = info.metadata.stringValue(QMediaMetaData::Duration); info.videoFrameRate = info.metadata.stringValue(QMediaMetaData::VideoFrameRate); info.videoCodec = info.metadata.stringValue(QMediaMetaData::VideoCodec); info.audioCodec = info.metadata.stringValue(QMediaMetaData::AudioCodec); } info.resolution = info.metadata.value(QMediaMetaData::Resolution).toSize(); info.fileFormat = info.metadata.stringValue(QMediaMetaData::FileFormat); } else { //timer ran out but mediaStatusChanged was not emitted qDebug() << "meta data is not valid: "<<file; info.metaDataIsValid = false; } if(info.mediaFileType == image) { //QMediaMetaData does not contain any media file creation date info key QMediaMetaData::Date so I must use exifinfo to at least get this for jpg media files FileManipulator fm; easyexif::EXIFInfo exifinfo = fm.getExifInfo(file); QString s = QString(exifinfo.DateTimeOriginal.c_str()); if(s.length()==19) //YY:MM:DD HH:MM:SS { QStringList d = QString(exifinfo.DateTimeOriginal.c_str()).split(" "); QStringList date = d.at(0).split(":"); QStringList time = d.at(1).split(":"); QDate cdate(date.at(0).toInt(),date.at(1).toInt(), date.at(2).toInt()); info.dt.setDate(cdate); QTime ctime(time.at(0).toInt(), time.at(1).toInt(), time.at(2).toInt()); info.dt.setTime(ctime); } else { qDebug() << "no creation date exif info available for file: "<< file <<", must use QFileInfo::metadataChangeTime(), even though this often fails to provide creation date of the image"; info.dt = f.metadataChangeTime(); } } pbValue = pbValue + pbSteps; setProgressBarValue(pbValue); return info; }
I am calling a member function of the class
MappedProcessor
(which has severalQFutureWatcher
members, each one for one particular purpose, in this case the future watcher accumulatesQFuture
which call the member functiongetMetaData
)The function getMetaData unfortunatelly needs a QEventLoop due to the fact that
QMediaMetaData
relies onQMediaPlayer
and its signalQMediaPlayer::mediaStatusChanged
to be sent until the meta data is available (I recycled this example https://stackoverflow.com/questions/69858215/read-qmediametadata-without-wait-after-set-the-media-source). So when callinggetMetaData
(and I call it up to 50k+ times withQtConcurrent::mapped
) , the event loop seems to be bound to main thread and the slot in connectionconnect(&player, &QMediaPlayer::mediaStatusChanged, &loop,&QEventLoop::quit);
never gets called.I could implement a non-member function that does the same, tht should not be an issue but I dont know how i can make sure that an
QEventLopp
in it gets associated with the thread that calls the function instead of the main thread.I filed a bug called [QMediaMetaData availability without having to wait for mediaStatusChanged() signal](link https://bugreports.qt.io/browse/QTBUG-120959) and I am looking for a workaround as not sure if this will ever be seen as bug or rather be seen as a low prio feature request.
Any ideas?
-
@hskoglund
I just tried, it did not work. I understand the bug now (at least I believe I do):https://www.toptal.com/qt/qt-multithreading-c-plus-plus
- Tasks that don’t need the event loop. Specifically, the tasks that are not using signal/slot mechanism during the task execution.
Use: QtConcurrent and QThreadPool + QRunnable. - Tasks that use signal/slots and therefore need the event loop.
Use: Worker objects moved to + QThread.
I was hoping to avoid digging deeper into multithreading and instead using
QtConcurrent::mapped()
but it seems I will have to dig deeper. - Tasks that don’t need the event loop. Specifically, the tasks that are not using signal/slot mechanism during the task execution.