From: Jean-Baptiste Mardelle Date: Thu, 13 Oct 2011 13:30:46 +0000 (+0000) Subject: Rewrote thread handling, should improve ui responsiveness X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=efb2133c58a7d4b2f53ca31b362c591a93c59290;p=kdenlive Rewrote thread handling, should improve ui responsiveness svn path=/trunk/kdenlive/; revision=5959 --- diff --git a/src/definitions.h b/src/definitions.h index 8f52d01b..b01ef370 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -69,6 +69,8 @@ struct TrackInfo { int duration; }; +typedef QMap stringMap; + struct ItemInfo { /** startPos is the position where the clip starts on the track */ GenTime startPos; diff --git a/src/kthumb.cpp b/src/kthumb.cpp index 7bc4d6f8..2f326b10 100644 --- a/src/kthumb.cpp +++ b/src/kthumb.cpp @@ -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() diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 11c42b57..69b88a59 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -149,7 +149,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & m_stopmotion(NULL) { qRegisterMetaType > (); - + qRegisterMetaType ("stringMap"); // Init locale QLocale systemLocale = QLocale(); systemLocale.setNumberOptions(QLocale::OmitGroupSeparator); @@ -846,9 +846,10 @@ void MainWindow::slotConnectMonitors() connect(m_projectList, SIGNAL(deleteProjectClips(QStringList, QMap)), this, SLOT(slotDeleteProjectClips(QStringList, QMap))); connect(m_projectList, SIGNAL(showClipProperties(DocClipBase *)), this, SLOT(slotShowClipProperties(DocClipBase *))); connect(m_projectList, SIGNAL(showClipProperties(QList , QMap)), this, SLOT(slotShowClipProperties(QList , QMap))); - 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))); diff --git a/src/projectlist.cpp b/src/projectlist.cpp index 3c972d20..4f413787 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -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::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); } diff --git a/src/projectlist.h b/src/projectlist.h index 575e1423..a5246de3 100644 --- a/src/projectlist.h +++ b/src/projectlist.h @@ -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 m_infoQueue; + //QMap m_infoQueue; QMap m_producerQueue; QList 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 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 , QMap commonproperties); diff --git a/src/renderer.cpp b/src/renderer.cpp index 40f0b9de..deab382b 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -43,12 +43,14 @@ #include #include #include +#include #include #include #include + 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: "<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()); diff --git a/src/renderer.h b/src/renderer.h index cb2183b1..afbf50eb 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -42,6 +42,7 @@ #include #include #include +#include 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 m_infoThread; + QList 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();