Humor me, will you?
constexpr int poolSize = 16;
QVector<QThread *> threadPool(poolSize);
QCoreApplication * app = QCoreApplication::instance();
for (size_t i = 0; i < poolSize; i++)  {
    QThread * thread = new QThread(app);
    thread->start();
    QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
}
QObject::connect(app, &QCoreApplication::aboutToQuit, [threadPool] () -> void {
    for (QThread * thread : threadPool)  {
        thread->quit();
        thread->wait();
    }
});
int index = 0;
for (const QString &video : videoFiles)
{
    ThumbnailExtractor *extractor = new ThumbnailExtractor(counter, folderPath + '/' + video);
    extractor->moveToThread(threadPool[index++ % poolSize]);
    QObject::connect(extractor, &ThumbnailExtractor::finished, extractor, &QObject::deleteLater);
    QMetaObject::invokeMethod(extractor, &ThumbnailExtractor::generateThumbnail, Qt::QueuedConnection);
}
A note here:
QMediaPlayer and related classes don't seem to be reentrant so you can't use them from different threads. Stick to the main one.