Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QtConcurrent::mapped with a member function with QEventLoop
Forum Updated to NodeBB v4.3 + New Features

QtConcurrent::mapped with a member function with QEventLoop

Scheduled Pinned Locked Moved Unsolved General and Desktop
3 Posts 2 Posters 284 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    DeSa
    wrote on 15 Jan 2024, 00:52 last edited by
    #1

    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 several QFutureWatcher members, each one for one particular purpose, in this case the future watcher accumulates QFuture which call the member function getMetaData)

    The function getMetaData unfortunatelly needs a QEventLoop due to the fact that QMediaMetaData relies on QMediaPlayer and its signal QMediaPlayer::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 calling getMetaData (and I call it up to 50k+ times with QtConcurrent::mapped) , the event loop seems to be bound to main thread and the slot in connection connect(&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?

    1 Reply Last reply
    0
    • H Offline
      H Offline
      hskoglund
      wrote on 15 Jan 2024, 04:52 last edited by
      #2

      Hi, just guessing but have you tried to explicitly queue the connection, e.g.

      ...
      connect(&player, &QMediaPlayer::mediaStatusChanged, &loop,&QEventLoop::quit, Qt::QueuedConnection);
      ...
      
      D 1 Reply Last reply 15 Jan 2024, 19:30
      0
      • H hskoglund
        15 Jan 2024, 04:52

        Hi, just guessing but have you tried to explicitly queue the connection, e.g.

        ...
        connect(&player, &QMediaPlayer::mediaStatusChanged, &loop,&QEventLoop::quit, Qt::QueuedConnection);
        ...
        
        D Offline
        D Offline
        DeSa
        wrote on 15 Jan 2024, 19:30 last edited by
        #3

        @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.

        1 Reply Last reply
        0

        1/3

        15 Jan 2024, 00:52

        • Login

        • Login or register to search.
        1 out of 3
        • First post
          1/3
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved