]> git.sesse.net Git - kdenlive/commitdiff
Rewrote thread handling, should improve ui responsiveness
authorJean-Baptiste Mardelle <jb@kdenlive.org>
Thu, 13 Oct 2011 13:30:46 +0000 (13:30 +0000)
committerJean-Baptiste Mardelle <jb@kdenlive.org>
Thu, 13 Oct 2011 13:30:46 +0000 (13:30 +0000)
svn path=/trunk/kdenlive/; revision=5959

src/definitions.h
src/kthumb.cpp
src/mainwindow.cpp
src/projectlist.cpp
src/projectlist.h
src/renderer.cpp
src/renderer.h

index 8f52d01b73b7c874720bd774e3a8a585bab06b70..b01ef370b1c25df5667f28d3a85abf8fb85bb525 100644 (file)
@@ -69,6 +69,8 @@ struct TrackInfo {
     int duration;
 };
 
+typedef QMap<QString, QString> stringMap;
+
 struct ItemInfo {
     /** startPos is the position where the clip starts on the track */
     GenTime startPos;
index 7bc4d6f82b419e53153f530f67ce5976bc1d7d70..2f326b1051b692f00134d193dc3a2a9a5fa811a1 100644 (file)
@@ -70,20 +70,19 @@ KThumb::~KThumb()
 
 void KThumb::setProducer(Mlt::Producer *producer)
 {
+    m_mutex.lock();
     m_requestedThumbs.clear();
     m_intraFramesQueue.clear();
     m_future.waitForFinished();
     m_intra.waitForFinished();
-    m_mutex.lock();
     m_producer = producer;
-    m_mutex.unlock();
     // FIXME: the profile() call leaks an object, but trying to free
     // it leads to a double-free in Profile::~Profile()
     if (producer) {
         m_dar = producer->profile()->dar();
         m_ratio = (double) producer->profile()->width() / producer->profile()->height();
     }
-        
+    m_mutex.unlock();
 }
 
 void KThumb::clearProducer()
index 11c42b57fd2b75feb93f6d9c8a60e5e08bbb54b3..69b88a599cb08ab680f01a3a813e9d608b6eff3f 100644 (file)
@@ -149,7 +149,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
     m_stopmotion(NULL)
 {   
     qRegisterMetaType<QVector<int16_t> > ();
-
+    qRegisterMetaType<stringMap> ("stringMap");
     // Init locale
     QLocale systemLocale = QLocale();
     systemLocale.setNumberOptions(QLocale::OmitGroupSeparator);
@@ -846,9 +846,10 @@ void MainWindow::slotConnectMonitors()
     connect(m_projectList, SIGNAL(deleteProjectClips(QStringList, QMap<QString, QString>)), this, SLOT(slotDeleteProjectClips(QStringList, QMap<QString, QString>)));
     connect(m_projectList, SIGNAL(showClipProperties(DocClipBase *)), this, SLOT(slotShowClipProperties(DocClipBase *)));
     connect(m_projectList, SIGNAL(showClipProperties(QList <DocClipBase *>, QMap<QString, QString>)), this, SLOT(slotShowClipProperties(QList <DocClipBase *>, QMap<QString, QString>)));
-    connect(m_projectList, SIGNAL(getFileProperties(const QDomElement, const QString &, int, bool, bool)), m_projectMonitor->render, SLOT(getFileProperties(const QDomElement, const QString &, int, bool, bool)));
-    connect(m_projectMonitor->render, SIGNAL(replyGetImage(const QString &, const QPixmap &)), m_projectList, SLOT(slotReplyGetImage(const QString &, const QPixmap &)));
-    connect(m_projectMonitor->render, SIGNAL(replyGetFileProperties(const QString &, Mlt::Producer*, const QMap < QString, QString > &, const QMap < QString, QString > &, bool, bool)), m_projectList, SLOT(slotReplyGetFileProperties(const QString &, Mlt::Producer*, const QMap < QString, QString > &, const QMap < QString, QString > &, bool, bool)));
+    connect(m_projectList, SIGNAL(getFileProperties(const QDomElement, const QString &, int, bool)), m_projectMonitor->render, SLOT(getFileProperties(const QDomElement, const QString &, int, bool)));
+    connect(m_projectMonitor->render, SIGNAL(replyGetImage(const QString &, const QString &, int, int)), m_projectList, SLOT(slotReplyGetImage(const QString &, const QString &, int, int)));
+    connect(m_projectMonitor->render, SIGNAL(replyGetImage(const QString &, const QImage &)), m_projectList, SLOT(slotReplyGetImage(const QString &, const QImage &)));
+    connect(m_projectMonitor->render, SIGNAL(replyGetFileProperties(const QString &, Mlt::Producer*, const stringMap &, const stringMap &, bool, bool)), m_projectList, SLOT(slotReplyGetFileProperties(const QString &, Mlt::Producer*, const stringMap &, const stringMap &, bool, bool)));
 
     connect(m_projectMonitor->render, SIGNAL(removeInvalidClip(const QString &, bool)), m_projectList, SLOT(slotRemoveInvalidClip(const QString &, bool)));
     
index 3c972d20402ba52fd81d9a4c1f5848513bfffd08..4f41378730f16e07d8205c0140ee6a19aadc48fb 100644 (file)
@@ -77,7 +77,6 @@ ProjectList::ProjectList(QWidget *parent) :
     m_transcodeAction(NULL),
     m_doc(NULL),
     m_refreshed(false),
-    m_infoQueue(),
     m_thumbnailQueue(),
     m_abortAllProxies(false)
 {
@@ -143,6 +142,7 @@ ProjectList::ProjectList(QWidget *parent) :
 ProjectList::~ProjectList()
 {
     m_abortAllProxies = true;
+    m_thumbnailQueue.clear();
     delete m_menu;
     m_listView->blockSignals(true);
     m_listView->clear();
@@ -475,7 +475,7 @@ void ProjectList::slotReloadClip(const QString &id)
                     e.setAttribute("length", length);
                 }
             }            
-            emit getFileProperties(e, item->clipId(), m_listView->iconSize().height(), true, false);
+            emit getFileProperties(e, item->clipId(), m_listView->iconSize().height(), true);
         }
     }
 }
@@ -1020,15 +1020,20 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
         item = new ProjectItem(m_listView, clip);
     }
     if (item->data(0, DurationRole).isNull()) item->setData(0, DurationRole, i18n("Loading"));
+    QString proxy = clip->getProperty("proxy");
+    if (!proxy.isEmpty() && proxy != "-") slotCreateProxy(clip->getId());
     connect(clip, SIGNAL(createProxy(const QString &)), this, SLOT(slotCreateProxy(const QString &)));
     connect(clip, SIGNAL(abortProxy(const QString &, const QString &)), this, SLOT(slotAbortProxy(const QString, const QString)));
     if (getProperties) {
+        int height = m_listView->iconSize().height();
+        int width = (int)(height  * m_render->dar());
+        QPixmap pix =  KIcon("video-x-generic").pixmap(QSize(width, height));
+        item->setData(0, Qt::DecorationRole, pix);
+        //item->setFlags(Qt::ItemIsSelectable);
         m_listView->processLayout();
         QDomElement e = clip->toXML().cloneNode().toElement();
         e.removeAttribute("file_hash");
-        m_mutex.lock();
-        m_infoQueue.insert(clip->getId(), e);
-        m_mutex.unlock();
+        emit getFileProperties(e, clip->getId(), m_listView->iconSize().height(), true);
     }
     else if (item->hasProxy() && !item->isProxyRunning()) {
         slotCreateProxy(clip->getId());
@@ -1074,11 +1079,6 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
     if (m_listView->isEnabled()) {
         updateButtons();
     }
-    
-    //if (getProperties && m_processingClips.isEmpty())
-        //m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    if (getProperties)
-        QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
 }
 
 void ProjectList::slotGotProxy(const QString &proxyPath)
@@ -1112,12 +1112,7 @@ void ProjectList::slotGotProxy(ProjectItem *item)
             e.setAttribute("length", length);
         }
     }
-    e.setAttribute("replace", 1);
-    m_mutex.lock();
-    m_infoQueue.insert(clip->getId(), e);
-    m_mutex.unlock();
-    //if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
+    emit getFileProperties(e, clip->getId(), m_listView->iconSize().height(), true);
 }
 
 void ProjectList::slotResetProjectList()
@@ -1125,41 +1120,13 @@ void ProjectList::slotResetProjectList()
     m_abortAllProxies = true;
     m_proxyThreads.waitForFinished();
     m_proxyThreads.clearFutures();
+    m_thumbnailQueue.clear();
     m_listView->clear();
     emit clipSelected(NULL);
-    m_thumbnailQueue.clear();
-    m_infoQueue.clear();
     m_refreshed = false;
     m_abortAllProxies = false;
 }
 
-void ProjectList::slotProcessNextClipInQueue()
-{
-    if (m_infoQueue.isEmpty()) {
-        emit processNextThumbnail();
-        return;
-    }
-    QMutexLocker locker(&m_mutex);
-    QMap<QString, QDomElement>::const_iterator j = m_infoQueue.constBegin();
-    if (j != m_infoQueue.constEnd()) {
-        QDomElement dom = j.value().cloneNode().toElement();
-        const QString id = j.key();
-        m_infoQueue.remove(id);
-        m_processingClips.append(id);
-        locker.unlock();
-        bool replace;
-        if (dom.hasAttribute("replace")) {
-            // Proxy action was enabled / disabled and we want to replace current producer
-            dom.removeAttribute("replace");
-            replace = true;
-        }
-        else replace = false;
-        bool selectClip = !replace;
-        if (m_infoQueue.count() > 1) selectClip = false;
-        emit getFileProperties(dom, id, m_listView->iconSize().height(), replace, selectClip);
-    }
-}
-
 void ProjectList::slotUpdateClip(const QString &id)
 {
     ProjectItem *item = getItemById(id);
@@ -1210,10 +1177,7 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged)
                         xml.removeAttribute("file_hash");
                         xml.removeAttribute("proxy_out");
                     }
-                    m_mutex.lock();
-                    m_infoQueue.insert(clip->getId(), xml);
-                    m_mutex.unlock();
-                    QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
+                    emit getFileProperties(xml, clip->getId(), m_listView->iconSize().height(), xml.attribute("replace") == "1");
                 }
                 else {
                     item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled);
@@ -1228,7 +1192,7 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged)
                         item->setData(0, Qt::DecorationRole, pixmap);
                     }
                 }
-            } else {
+            } else {              
                 if (displayRatioChanged || item->data(0, Qt::DecorationRole).isNull())
                     requestClipThumbnail(clip->getId());
                 if (item->data(0, DurationRole).toString().isEmpty()) {
@@ -1250,17 +1214,11 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged)
         }
         ++it;
     }
-
-    //if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    //QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    /*while (!m_infoQueue.isEmpty()) {
-        QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    }*/
     
     if (m_listView->isEnabled())
         monitorItemEditing(true);
     m_listView->setSortingEnabled(true);
-    if (m_infoQueue.isEmpty()) {
+    if (m_render->processingItems() == 0) {
        slotProcessNextThumbnail();
     }
 }
@@ -1372,9 +1330,8 @@ void ProjectList::slotRemoveInvalidClip(const QString &id, bool replace)
     ProjectItem *item = getItemById(id);
     m_processingClips.removeAll(id);
     m_thumbnailQueue.removeAll(id);
-    //if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);   
     if (item) {
+        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
         const QString path = item->referencedClip()->fileURL().path();
         if (item->referencedClip()->isPlaceHolder()) replace = false;
         if (!path.isEmpty()) {
@@ -1392,6 +1349,7 @@ void ProjectList::slotRemoveInvalidProxy(const QString &id, bool durationError)
 {
     ProjectItem *item = getItemById(id);
     if (item) {
+        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
         if (durationError) {
             kDebug() << "Proxy duration is wrong, try changing transcoding parameters.";
             emit displayMessage(i18n("Proxy clip unusable (duration is different from original)."), -2);
@@ -1407,8 +1365,6 @@ void ProjectList::slotRemoveInvalidProxy(const QString &id, bool durationError)
     }
     m_processingClips.removeAll(id);
     m_thumbnailQueue.removeAll(id);
-    //if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
 }
 
 void ProjectList::slotAddColorClip()
@@ -1520,12 +1476,12 @@ void ProjectList::setDocument(KdenliveDoc *doc)
     m_abortAllProxies = true;
     m_proxyThreads.waitForFinished();
     m_proxyThreads.clearFutures();
+    m_thumbnailQueue.clear();
     m_listView->clear();
     m_processingClips.clear();
+    
     m_listView->setSortingEnabled(false);
     emit clipSelected(NULL);
-    m_thumbnailQueue.clear();
-    m_infoQueue.clear();
     m_refreshed = false;
     m_fps = doc->fps();
     m_timecode = doc->timecode();
@@ -1584,7 +1540,7 @@ QDomElement ProjectList::producersList()
 
 void ProjectList::slotCheckForEmptyQueue()
 {
-    if (m_processingClips.isEmpty() && m_thumbnailQueue.isEmpty() && m_infoQueue.isEmpty()) {
+    if (m_render->processingItems() == 0 && m_thumbnailQueue.isEmpty()) {
         if (!m_refreshed) {
             emit loadingIsOver();
             emit displayMessage(QString(), -1);
@@ -1602,22 +1558,20 @@ void ProjectList::slotCheckForEmptyQueue()
 void ProjectList::requestClipThumbnail(const QString id)
 {
     if (!m_thumbnailQueue.contains(id)) m_thumbnailQueue.append(id);
+    slotProcessNextThumbnail();
 }
 
 void ProjectList::slotProcessNextThumbnail()
 {
-    if (m_thumbnailQueue.isEmpty() && m_infoQueue.isEmpty()) {
-        slotCheckForEmptyQueue();
+    if (m_render->processingItems() > 0) {
         return;
     }
-    if (!m_infoQueue.isEmpty()) {
-        //QTimer::singleShot(300, this, SLOT(slotProcessNextThumbnail()));
+    if (m_thumbnailQueue.isEmpty()) {
+        slotCheckForEmptyQueue();
         return;
     }
-    if (m_thumbnailQueue.count() > 1) {
-        int max = m_doc->clipManager()->clipsCount();
-        emit displayMessage(i18n("Loading thumbnails"), (int)(100 *(max - m_thumbnailQueue.count()) / max));
-    }
+    int max = m_doc->clipManager()->clipsCount();
+    emit displayMessage(i18n("Loading thumbnails"), (int)(100 *(max - m_thumbnailQueue.count()) / max));
     slotRefreshClipThumbnail(m_thumbnailQueue.takeFirst(), false);
 }
 
@@ -1680,28 +1634,30 @@ void ProjectList::slotRefreshClipThumbnail(QTreeWidgetItem *it, bool update)
     }
 }
 
-void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const QMap < QString, QString > &properties, const QMap < QString, QString > &metadata, bool replace, bool selectClip)
+void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const stringMap &properties, const stringMap &metadata, bool replace, bool refreshThumbnail)
 {
     QString toReload;
     ProjectItem *item = getItemById(clipId);
-    if (!m_refreshed) {
-        // we are still finishing to load the document
-        selectClip = false;
+
+    int queue = m_render->processingItems();
+    if (queue == 0) {
+        m_listView->setEnabled(true);
     }
-    m_processingClips.removeAll(clipId);
-    if (m_infoQueue.isEmpty() && m_processingClips.isEmpty()) m_listView->setEnabled(true);
     if (item && producer) {
         //m_listView->blockSignals(true);
         monitorItemEditing(false);
         DocClipBase *clip = item->referencedClip();
         item->setProperties(properties, metadata);
-        if (clip->isPlaceHolder() && producer->is_valid()) {
-            clip->setValid();
+        if (producer->is_valid()) {
+            if (clip->isPlaceHolder()) {
+                clip->setValid();
+                toReload = clipId;
+            }
             item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
-            toReload = clipId;
         }
         clip->setProducer(producer, replace);
         clip->askForAudioThumbs();
+        if (refreshThumbnail) m_thumbnailQueue.append(clipId);
         // Proxy stuff
         QString size = properties.value("frame_size");
         if (!useProxy() && clip->getProperty("proxy").isEmpty()) setProxyStatus(item, NOPROXY);
@@ -1737,8 +1693,8 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
         if (m_listView->isEnabled())
             monitorItemEditing(true);
     } else kDebug() << "////////  COULD NOT FIND CLIP TO UPDATE PRPS...";
-    if (selectClip && m_infoQueue.isEmpty()) {
-        if (item && m_infoQueue.isEmpty() && m_thumbnailQueue.isEmpty() && m_processingClips.isEmpty()) {
+    if (queue == 0) {
+        if (item && m_thumbnailQueue.isEmpty()) {
             m_listView->setCurrentItem(item);
             bool updatedProfile = false;
             if (item->parent()) {
@@ -1753,8 +1709,9 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
             }
         } else {
             int max = m_doc->clipManager()->clipsCount();
-            emit displayMessage(i18n("Loading clips"), (int)(100 *(max - m_infoQueue.count()) / max));
+            emit displayMessage(i18n("Loading clips"), (int)(100 *(max - queue) / max));
         }
+        processNextThumbnail();
     }
     if (item && m_listView->isEnabled() && replace) {
             // update clip in clip monitor
@@ -1765,9 +1722,6 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
         }
     if (!toReload.isEmpty())
         emit clipNeedsReload(toReload, true);
-
-    //if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
 }
 
 bool ProjectList::adjustProjectProfileToItem(ProjectItem *item)
@@ -1849,13 +1803,26 @@ bool ProjectList::generateImageProxy() const
     return m_doc->getDocumentProperty("generateimageproxy").toInt();
 }
 
-void ProjectList::slotReplyGetImage(const QString &clipId, const QPixmap &pix)
+void ProjectList::slotReplyGetImage(const QString &clipId, const QImage &img)
+{
+    QPixmap pix = QPixmap::fromImage(img);
+    setThumbnail(clipId, pix);
+}
+
+void ProjectList::slotReplyGetImage(const QString &clipId, const QString &name, int width, int height)
+{
+    QPixmap pix =  KIcon(name).pixmap(QSize(width, height));
+    setThumbnail(clipId, pix);
+}
+
+void ProjectList::setThumbnail(const QString &clipId, const QPixmap &pix)
 {
     ProjectItem *item = getItemById(clipId);
     if (item && !pix.isNull()) {
         monitorItemEditing(false);
         item->setData(0, Qt::DecorationRole, pix);
         monitorItemEditing(true);
+        //update();
         m_doc->cachePixmap(item->getClipHash(), pix);
         if (m_listView->isEnabled())
             m_listView->blockSignals(false);
@@ -2125,9 +2092,7 @@ void ProjectList::doUpdateClipCut(const QString &id, const QPoint oldzone, const
 
 void ProjectList::slotForceProcessing(const QString &id)
 {
-    while (m_infoQueue.contains(id)) {
-        slotProcessNextClipInQueue();
-    }
+    m_render->forceProcessing(id);
 }
 
 void ProjectList::slotAddOrUpdateSequence(const QString frameName)
@@ -2507,8 +2472,6 @@ void ProjectList::updateProxyConfig()
     }
     if (command->childCount() > 0) m_doc->commandStack()->push(command);
     else delete command;
-    //if (!m_infoQueue.isEmpty() && !m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
 }
 
 void ProjectList::slotProxyCurrentItem(bool doProxy)
@@ -2556,8 +2519,6 @@ void ProjectList::slotProxyCurrentItem(bool doProxy)
         m_doc->commandStack()->push(command);
     }
     else delete command;
-    //if (!m_infoQueue.isEmpty() && !m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
-    if (!m_infoQueue.isEmpty()) QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue);
 }
 
 
index 575e14238a59c44171888f7e6152a24b51ebd860..a5246de37dec7afc39166bd75481742a24af1747 100644 (file)
@@ -225,8 +225,9 @@ public:
 public slots:
     void setDocument(KdenliveDoc *doc);
     void updateAllClips(bool displayRatioChanged, bool fpsChanged);
-    void slotReplyGetImage(const QString &clipId, const QPixmap &pix);
-    void slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const QMap < QString, QString > &properties, const QMap < QString, QString > &metadata, bool replace, bool selectClip);
+    void slotReplyGetImage(const QString &clipId, const QImage &img);
+    void slotReplyGetImage(const QString &clipId, const QString &name, int width, int height);
+    void slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const stringMap &properties, const stringMap &metadata, bool replace, bool refreshThumbnail);
     void slotAddClip(DocClipBase *clip, bool getProperties);
     void slotDeleteClip(const QString &clipId);
     void slotUpdateClip(const QString &id);
@@ -279,7 +280,7 @@ private:
     QToolButton *m_addButton;
     QToolButton *m_deleteButton;
     QToolButton *m_editButton;
-    QMap <QString, QDomElement> m_infoQueue;
+    //QMap <QString, QDomElement> m_infoQueue;
     QMap <QString, QDomElement> m_producerQueue;
     QList <QString> m_thumbnailQueue;
     QAction *m_proxyAction;
@@ -319,6 +320,8 @@ private:
     /** @brief Process ffmpeg output to find out process progress. */
     void processLogInfo(const QString &path, int *duration, const QString &log);
     void monitorItemEditing(bool enable);
+    /** @brief Set thumbnail for a project's clip. */
+    void setThumbnail(const QString &clipId, const QPixmap &pix);
 
 private slots:
     void slotClipSelected();
@@ -337,7 +340,6 @@ private slots:
     /** @brief This is triggered when a clip description has been modified. */
     void slotItemEdited(QTreeWidgetItem *item, int column);
     void slotUpdateClipProperties(ProjectItem *item, QMap <QString, QString> properties);
-    void slotProcessNextClipInQueue();
     void slotProcessNextThumbnail();
     void slotCheckForEmptyQueue();
     void slotPauseMonitor();
@@ -363,7 +365,7 @@ private slots:
 
 signals:
     void clipSelected(DocClipBase *, QPoint zone = QPoint());
-    void getFileProperties(const QDomElement, const QString &, int pixHeight, bool, bool);
+    void getFileProperties(const QDomElement, const QString &, int pixHeight, bool);
     void receivedClipDuration(const QString &);
     void showClipProperties(DocClipBase *);
     void showClipProperties(QList <DocClipBase *>, QMap<QString, QString> commonproperties);
index 40f0b9de3f91a8ab25b5ee529d344cd9c1df1f14..deab382ba5a3b94cc0c4207ca878425f8243fe9b 100644 (file)
 #include <QDir>
 #include <QString>
 #include <QApplication>
+#include <QtConcurrentRun>
 
 #include <cstdlib>
 #include <cstdarg>
 
 #include <QDebug>
 
+
 static void kdenlive_callback(void* /*ptr*/, int level, const char* fmt, va_list vl)
 {
 //     kDebug() << "log level" << level << QString().vsprintf(fmt, vl).simplified();
@@ -115,7 +117,6 @@ Render::Render(const QString & rendererName, int winid, QString profile, QWidget
 {
     if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
     buildConsumer(profile);
-
     m_mltProducer = m_blackClip->cut(0, 50);
     m_mltConsumer->connect(*m_mltProducer);
     m_mltProducer->set_speed(0.0);
@@ -131,6 +132,8 @@ Render::~Render()
 void Render::closeMlt()
 {       
     //delete m_osdTimer;
+    m_requestList.clear();
+    m_infoThread.waitForFinished();
     if (m_mltConsumer) delete m_mltConsumer;
     if (m_mltProducer) delete m_mltProducer;
     /*if (m_mltProducer) {
@@ -317,6 +320,8 @@ int Render::resetProfile(const QString &profileName, bool dropSceneList)
     double current_dar = m_mltProfile->dar();
     delete m_blackClip;
     m_blackClip = NULL;
+    m_requestList.clear();
+    m_infoThread.waitForFinished();
 
     if (m_mltProducer) {
         pos = m_mltProducer->position();
@@ -548,47 +553,86 @@ void Render::slotSplitView(bool doit)
     }
 }
 
-void Render::getFileProperties(const QDomElement &xml, const QString &clipId, int imageHeight, bool replaceProducer, bool selectClip)
+void Render::getFileProperties(const QDomElement &xml, const QString &clipId, int imageHeight, bool replaceProducer)
+{
+    requestClipInfo info;
+    info.xml = xml;
+    info.clipId = clipId;
+    info.imageHeight = imageHeight;
+    info.replaceProducer = replaceProducer;
+    m_requestList.append(info);
+    if (!m_infoThread.isRunning())
+        m_infoThread = QtConcurrent::run(this, &Render::getFileProperties2);
+}
+
+void Render::forceProcessing(const QString &id)
+{
+    m_infoMutex.lock();
+    for (int i = 0; i < m_requestList.count(); i++) {
+        requestClipInfo info = m_requestList.at(i);
+        if (info.clipId == id) {
+            if (i == 0) break;
+            else {
+                m_requestList.removeAt(i);
+                m_requestList.prepend(info);
+                break;
+            }
+        }
+    }
+    m_infoMutex.unlock();
+}
+
+int Render::processingItems() const
 {
+    int count = m_requestList.count();
+    if (m_infoThread.isRunning()) count++;
+    return count;
+}
+
+void Render::getFileProperties2()
+{
+    while (!m_requestList.isEmpty()) {
+    m_infoMutex.lock();
+    requestClipInfo info = m_requestList.takeFirst();
+    m_infoMutex.unlock();
+    
     QString path;
     QLocale locale;
     bool proxyProducer;
-    if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") {
-        path = xml.attribute("proxy");
+    if (info.xml.hasAttribute("proxy") && info.xml.attribute("proxy") != "-") {
+        path = info.xml.attribute("proxy");
         proxyProducer = true;
     }
     else {
-        path = xml.attribute("resource");
+        path = info.xml.attribute("resource");
         proxyProducer = false;
     }
 
     KUrl url(path);
     Mlt::Producer *producer = NULL;
-    CLIPTYPE type = (CLIPTYPE)xml.attribute("type").toInt();
-    //kDebug() << "PROFILE WIDT: "<< xml.attribute("mlt_service") << ": "<< m_mltProfile->width() << "\n...................\n\n";
-    /*if (xml.attribute("type").toInt() == TEXT && !QFile::exists(url.path())) {
+    CLIPTYPE type = (CLIPTYPE)info.xml.attribute("type").toInt();
+    //kDebug() << "PROFILE WIDT: "<< info.xml.attribute("mlt_service") << ": "<< m_mltProfile->width() << "\n...................\n\n";
+    /*if (info.xml.attribute("type").toInt() == TEXT && !QFile::exists(url.path())) {
         emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer);
         return;
     }*/
 
     if (type == COLOR) {
-        producer = new Mlt::Producer(*m_mltProfile, 0, ("colour:" + xml.attribute("colour")).toUtf8().constData());
+        producer = new Mlt::Producer(*m_mltProfile, 0, ("colour:" + info.xml.attribute("colour")).toUtf8().constData());
     } else if (type == TEXT) {
-        producer = new Mlt::Producer(*m_mltProfile, 0, ("kdenlivetitle:" + xml.attribute("resource")).toUtf8().constData());
-        if (producer && producer->is_valid() && xml.hasAttribute("xmldata"))
-            producer->set("xmldata", xml.attribute("xmldata").toUtf8().constData());
+        producer = new Mlt::Producer(*m_mltProfile, 0, ("kdenlivetitle:" + info.xml.attribute("resource")).toUtf8().constData());
+        if (producer && producer->is_valid() && info.xml.hasAttribute("xmldata"))
+            producer->set("xmldata", info.xml.attribute("xmldata").toUtf8().constData());
     } else if (url.isEmpty()) {
         QDomDocument doc;
         QDomElement mlt = doc.createElement("mlt");
         QDomElement play = doc.createElement("playlist");
         doc.appendChild(mlt);
         mlt.appendChild(play);
-        play.appendChild(doc.importNode(xml, true));
+        play.appendChild(doc.importNode(info.xml, true));
         producer = new Mlt::Producer(*m_mltProfile, "xml-string", doc.toString().toUtf8().constData());
     } else {
-        char *resTag = qstrdup(QString("nocache:" + path).toUtf8().constData());
         producer = new Mlt::Producer(*m_mltProfile, path.toUtf8().constData());
-        delete[] resTag;
     }
 
 
@@ -596,113 +640,110 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId, in
         kDebug() << " / / / / / / / / ERROR / / / / // CANNOT LOAD PRODUCER: "<<path;
         if (proxyProducer) {
             // Proxy file is corrupted
-            emit removeInvalidProxy(clipId, false);
+            emit removeInvalidProxy(info.clipId, false);
         }
-        else emit removeInvalidClip(clipId, replaceProducer);
+        else emit removeInvalidClip(info.clipId, info.replaceProducer);
         delete producer;
-        return;
+        continue;
     }
 
-    if (proxyProducer && xml.hasAttribute("proxy_out")) {
-        producer->set("length", xml.attribute("proxy_out").toInt() + 1);
-        producer->set("out", xml.attribute("proxy_out").toInt());
-        if (producer->get_out() != xml.attribute("proxy_out").toInt()) {
+    if (proxyProducer && info.xml.hasAttribute("proxy_out")) {
+        producer->set("length", info.xml.attribute("proxy_out").toInt() + 1);
+        producer->set("out", info.xml.attribute("proxy_out").toInt());
+        if (producer->get_out() != info.xml.attribute("proxy_out").toInt()) {
             // Proxy file length is different than original clip length, this will corrupt project so disable this proxy clip
-            emit removeInvalidProxy(clipId, true);
+            emit removeInvalidProxy(info.clipId, true);
             delete producer;
-            return;
+            continue;
         }
     }
 
-    if (xml.hasAttribute("force_aspect_ratio")) {
-        double aspect = xml.attribute("force_aspect_ratio").toDouble();
+    if (info.xml.hasAttribute("force_aspect_ratio")) {
+        double aspect = info.xml.attribute("force_aspect_ratio").toDouble();
         if (aspect > 0) producer->set("force_aspect_ratio", aspect);
     }
 
-    if (xml.hasAttribute("force_aspect_num") && xml.hasAttribute("force_aspect_den")) {
-        int width = xml.attribute("frame_size").section('x', 0, 0).toInt();
-        int height = xml.attribute("frame_size").section('x', 1, 1).toInt();
-        int aspectNumerator = xml.attribute("force_aspect_num").toInt();
-        int aspectDenominator = xml.attribute("force_aspect_den").toInt();
+    if (info.xml.hasAttribute("force_aspect_num") && info.xml.hasAttribute("force_aspect_den")) {
+        int width = info.xml.attribute("frame_size").section('x', 0, 0).toInt();
+        int height = info.xml.attribute("frame_size").section('x', 1, 1).toInt();
+        int aspectNumerator = info.xml.attribute("force_aspect_num").toInt();
+        int aspectDenominator = info.xml.attribute("force_aspect_den").toInt();
         if (aspectDenominator != 0 && width != 0)
             producer->set("force_aspect_ratio", double(height) * aspectNumerator / aspectDenominator / width);
     }
 
-    if (xml.hasAttribute("force_fps")) {
-        double fps = xml.attribute("force_fps").toDouble();
+    if (info.xml.hasAttribute("force_fps")) {
+        double fps = info.xml.attribute("force_fps").toDouble();
         if (fps > 0) producer->set("force_fps", fps);
     }
 
-    if (xml.hasAttribute("force_progressive")) {
+    if (info.xml.hasAttribute("force_progressive")) {
         bool ok;
-        int progressive = xml.attribute("force_progressive").toInt(&ok);
+        int progressive = info.xml.attribute("force_progressive").toInt(&ok);
         if (ok) producer->set("force_progressive", progressive);
     }
-    if (xml.hasAttribute("force_tff")) {
+    if (info.xml.hasAttribute("force_tff")) {
         bool ok;
-        int fieldOrder = xml.attribute("force_tff").toInt(&ok);
+        int fieldOrder = info.xml.attribute("force_tff").toInt(&ok);
         if (ok) producer->set("force_tff", fieldOrder);
     }
-    if (xml.hasAttribute("threads")) {
-        int threads = xml.attribute("threads").toInt();
+    if (info.xml.hasAttribute("threads")) {
+        int threads = info.xml.attribute("threads").toInt();
         if (threads != 1) producer->set("threads", threads);
     }
-    if (xml.hasAttribute("video_index")) {
-        int vindex = xml.attribute("video_index").toInt();
+    if (info.xml.hasAttribute("video_index")) {
+        int vindex = info.xml.attribute("video_index").toInt();
         if (vindex != 0) producer->set("video_index", vindex);
     }
-    if (xml.hasAttribute("audio_index")) {
-        int aindex = xml.attribute("audio_index").toInt();
+    if (info.xml.hasAttribute("audio_index")) {
+        int aindex = info.xml.attribute("audio_index").toInt();
         if (aindex != 0) producer->set("audio_index", aindex);
     }
-    if (xml.hasAttribute("force_colorspace")) {
-        int colorspace = xml.attribute("force_colorspace").toInt();
+    if (info.xml.hasAttribute("force_colorspace")) {
+        int colorspace = info.xml.attribute("force_colorspace").toInt();
         if (colorspace != 0) producer->set("force_colorspace", colorspace);
     }
-    if (xml.hasAttribute("full_luma")) {
-        int full_luma = xml.attribute("full_luma").toInt();
+    if (info.xml.hasAttribute("full_luma")) {
+        int full_luma = info.xml.attribute("full_luma").toInt();
         if (full_luma != 0) producer->set("set.force_full_luma", full_luma);
     }
 
     int clipOut = 0;
     int duration = 0;
-    if (xml.hasAttribute("out")) clipOut = xml.attribute("out").toInt();
+    if (info.xml.hasAttribute("out")) clipOut = info.xml.attribute("out").toInt();
 
     // setup length here as otherwise default length (currently 15000 frames in MLT) will be taken even if outpoint is larger
     if (type == COLOR || type == TEXT || type == IMAGE || type == SLIDESHOW) {
         int length;
-        if (xml.hasAttribute("length")) {
+        if (info.xml.hasAttribute("length")) {
             if (clipOut > 0) duration = clipOut + 1;
-            length = xml.attribute("length").toInt();
+            length = info.xml.attribute("length").toInt();
             clipOut = length - 1;
         }
-        else length = xml.attribute("out").toInt() - xml.attribute("in").toInt();
+        else length = info.xml.attribute("out").toInt() - info.xml.attribute("in").toInt();
         producer->set("length", length);
     }
 
-    if (clipOut > 0) producer->set_in_and_out(xml.attribute("in").toInt(), clipOut);
+    if (clipOut > 0) producer->set_in_and_out(info.xml.attribute("in").toInt(), clipOut);
 
-    producer->set("id", clipId.toUtf8().constData());
+    producer->set("id", info.clipId.toUtf8().constData());
 
-    if (xml.hasAttribute("templatetext"))
-        producer->set("templatetext", xml.attribute("templatetext").toUtf8().constData());
+    if (info.xml.hasAttribute("templatetext"))
+        producer->set("templatetext", info.xml.attribute("templatetext").toUtf8().constData());
 
-    if ((!replaceProducer && xml.hasAttribute("file_hash")) || xml.hasAttribute("proxy")) {
+    if ((!info.replaceProducer && info.xml.hasAttribute("file_hash")) || info.xml.hasAttribute("proxy")) {
         // Clip  already has all properties
-        if (replaceProducer) emit blockClipMonitor(clipId);
-        // Querying a frame is required by MLT, otherwise the producer is not correctly initialised
-        //Mlt::Frame *frame = producer->get_frame();
-        //delete frame;
-        emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer, selectClip);
-        return;
+        if (info.replaceProducer) emit blockClipMonitor(info.clipId);
+        emit replyGetFileProperties(info.clipId, producer, stringMap(), stringMap(), info.replaceProducer, true);
+        continue;
     }
 
-    int imageWidth = (int)((double) imageHeight * m_mltProfile->width() / m_mltProfile->height() + 0.5);
-    int fullWidth = (int)((double) imageHeight * m_mltProfile->dar() + 0.5);
-    QMap < QString, QString > filePropertyMap;
-    QMap < QString, QString > metadataPropertyMap;
+    int imageWidth = (int)((double) info.imageHeight * m_mltProfile->width() / m_mltProfile->height() + 0.5);
+    int fullWidth = (int)((double) info.imageHeight * m_mltProfile->dar() + 0.5);
+    stringMap filePropertyMap;
+    stringMap metadataPropertyMap;
 
-    int frameNumber = xml.attribute("thumbnail", "0").toInt();
+    int frameNumber = info.xml.attribute("thumbnail", "0").toInt();
     if (frameNumber != 0) producer->seek(frameNumber);
 
     filePropertyMap["duration"] = QString::number(duration > 0 ? duration : producer->get_playtime());
@@ -711,15 +752,15 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId, in
     Mlt::Frame *frame = producer->get_frame();
 
     if (type == SLIDESHOW) {
-        int ttl = xml.hasAttribute("ttl") ? xml.attribute("ttl").toInt() : 0;
+        int ttl = info.xml.hasAttribute("ttl") ? info.xml.attribute("ttl").toInt() : 0;
         if (ttl) producer->set("ttl", ttl);
-        if (!xml.attribute("animation").isEmpty()) {
+        if (!info.xml.attribute("animation").isEmpty()) {
             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "affine");
             if (filter && filter->is_valid()) {
                 int cycle = ttl;
-                QString geometry = SlideshowClip::animationToGeometry(xml.attribute("animation"), cycle);
+                QString geometry = SlideshowClip::animationToGeometry(info.xml.attribute("animation"), cycle);
                 if (!geometry.isEmpty()) {
-                    if (xml.attribute("animation").contains("low-pass")) {
+                    if (info.xml.attribute("animation").contains("low-pass")) {
                         Mlt::Filter *blur = new Mlt::Filter(*m_mltProfile, "boxblur");
                         if (blur && blur->is_valid())
                             producer->attach(*blur);
@@ -730,23 +771,23 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId, in
                 }
             }
         }
-        if (xml.attribute("fade") == "1") {
+        if (info.xml.attribute("fade") == "1") {
             // user wants a fade effect to slideshow
             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "luma");
             if (filter && filter->is_valid()) {
                 if (ttl) filter->set("cycle", ttl);
-                if (xml.hasAttribute("luma_duration") && !xml.attribute("luma_duration").isEmpty()) filter->set("duration", xml.attribute("luma_duration").toInt());
-                if (xml.hasAttribute("luma_file") && !xml.attribute("luma_file").isEmpty()) {
-                    filter->set("luma.resource", xml.attribute("luma_file").toUtf8().constData());
-                    if (xml.hasAttribute("softness")) {
-                        int soft = xml.attribute("softness").toInt();
+                if (info.xml.hasAttribute("luma_duration") && !info.xml.attribute("luma_duration").isEmpty()) filter->set("duration", info.xml.attribute("luma_duration").toInt());
+                if (info.xml.hasAttribute("luma_file") && !info.xml.attribute("luma_file").isEmpty()) {
+                    filter->set("luma.resource", info.xml.attribute("luma_file").toUtf8().constData());
+                    if (info.xml.hasAttribute("softness")) {
+                        int soft = info.xml.attribute("softness").toInt();
                         filter->set("luma.softness", (double) soft / 100.0);
                     }
                 }
                 producer->attach(*filter);
             }
         }
-        if (xml.attribute("crop") == "1") {
+        if (info.xml.attribute("crop") == "1") {
             // user wants to center crop the slides
             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "crop");
             if (filter && filter->is_valid()) {
@@ -779,7 +820,7 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId, in
             QImage img;
             do {
                 variance = 100;
-                img = KThumb::getFrame(frame, imageWidth, fullWidth, imageHeight);
+                img = KThumb::getFrame(frame, imageWidth, fullWidth, info.imageHeight);
                 variance = KThumb::imageVariance(img);
                 if (frameNumber == 0 && variance< 6) {
                     // Thumbnail is not interesting (for example all black, seek to fetch better thumb
@@ -790,12 +831,9 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId, in
                     variance = -1;
                 }
             } while (variance == -1);
-            QPixmap pix = QPixmap::fromImage(img);
-            emit replyGetImage(clipId, pix);
-
+            emit replyGetImage(info.clipId, img);
         } else if (frame->get_int("test_audio") == 0) {
-            QPixmap pixmap = KIcon("audio-x-generic").pixmap(QSize(fullWidth, imageHeight));
-            emit replyGetImage(clipId, pixmap);
+            emit replyGetImage(info.clipId, "audio-x-generic", fullWidth, info.imageHeight);
             filePropertyMap["type"] = "audio";
         }
     }
@@ -870,10 +908,11 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId, in
             metadataPropertyMap[ name.section('.', 0, -2)] = value;
     }
     producer->seek(0);
-    if (replaceProducer) emit blockClipMonitor(clipId);
-    emit replyGetFileProperties(clipId, producer, filePropertyMap, metadataPropertyMap, replaceProducer, selectClip);
+    if (info.replaceProducer) emit blockClipMonitor(info.clipId);
+    emit replyGetFileProperties(info.clipId, producer, filePropertyMap, metadataPropertyMap, info.replaceProducer);
     // FIXME: should delete this to avoid a leak...
     //delete producer;
+    }
 }
 
 
@@ -985,6 +1024,8 @@ int Render::setSceneList(QString playlist, int position)
         kWarning() << "///////  ERROR, TRYING TO USE NULL MLT CONSUMER";
         error = -1;
     }
+    m_requestList.clear();
+    m_infoThread.waitForFinished();
 
     if (m_mltProducer) {
         m_mltProducer->set_speed(0);
@@ -1031,7 +1072,6 @@ int Render::setSceneList(QString playlist, int position)
     }
 
     blockSignals(true);
-
     m_locale = QLocale();
 
     m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", playlist.toUtf8().constData());
index cb2183b1bb12a275c32e88e216bf93229591efe6..afbf50eb78164ea860f0bdc7fec0e201efc02ad1 100644 (file)
@@ -42,6 +42,7 @@
 #include <QList>
 #include <QEvent>
 #include <QMutex>
+#include <QFuture>
 
 class QTimer;
 class QPixmap;
@@ -60,6 +61,13 @@ class Profile;
 class Service;
 };
 
+struct requestClipInfo {
+    QDomElement xml;
+    QString clipId;
+    int imageHeight;
+    bool replaceProducer;
+};
+
 class MltErrorEvent : public QEvent
 {
 public:
@@ -270,6 +278,10 @@ Q_OBJECT public:
 
     /** @brief Returns a pointer to the main producer. */
     Mlt::Producer *getProducer();
+    /** @brief Returns the number of clips to process (When requesting clip info). */
+    int processingItems() const;
+    /** @brief Force processing of clip with selected id. */
+    void forceProcessing(const QString &id);
 
 private:
 
@@ -302,11 +314,14 @@ private:
 
     QTimer *m_osdTimer;
     QMutex m_mutex;
+    QMutex m_infoMutex;
 
     /** @brief A human-readable description of this renderer. */
     int m_winid;
 
     QLocale m_locale;
+    QFuture <void> m_infoThread;
+    QList <requestClipInfo> m_requestList;
 
     void closeMlt();
     void mltCheckLength(Mlt::Tractor *tractor);
@@ -337,10 +352,11 @@ private slots:
 signals:
 
     /** @brief The renderer received a reply to a getFileProperties request. */
-    void replyGetFileProperties(const QString &clipId, Mlt::Producer*, const QMap < QString, QString > &, const QMap < QString, QString > &, bool, bool);
+    void replyGetFileProperties(const QString &clipId, Mlt::Producer*, const stringMap &, const stringMap &, bool replaceProducer, bool refreshThumbnail = false);
 
     /** @brief The renderer received a reply to a getImage request. */
-    void replyGetImage(const QString &, const QPixmap &);
+    void replyGetImage(const QString &, const QString &, int, int);
+    void replyGetImage(const QString &, const QImage &);
 
     /** @brief The renderer stopped, either playing or rendering. */
     void stopped();
@@ -394,7 +410,9 @@ public slots:
         @param selectClip If true, clip item will be selected in project view
      * Upon return, the result will be emitted via replyGetFileProperties().
      * Wraps the VEML command of the same name. */
-    void getFileProperties(const QDomElement &xml, const QString &clipId, int imageHeight, bool replaceProducer = true, bool selectClip = false);
+    void getFileProperties(const QDomElement &xml, const QString &clipId, int imageHeight, bool replaceProducer = true);
+
+    void getFileProperties2();
 
     void exportFileToFirewire(QString srcFileName, int port, GenTime startTime, GenTime endTime);
     void mltSavePlaylist();