From 67202227a6d1c64ac1112273db86c6329843358a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Thu, 17 Feb 2011 11:21:05 +0000 Subject: [PATCH] Big update to the proxy clips, fixing several issues. They can now be deleted in clip properties svn path=/trunk/kdenlive/; revision=5416 --- src/clipproperties.cpp | 36 +++++ src/clipproperties.h | 3 + src/definitions.h | 2 + src/docclipbase.cpp | 78 ++-------- src/docclipbase.h | 14 +- src/kdenlivedoc.cpp | 4 +- src/kdenlivesettings.kcfg | 10 ++ src/kdenlivesettingsdialog.cpp | 10 ++ src/mainwindow.cpp | 28 +++- src/projectitem.cpp | 24 +-- src/projectitem.h | 6 +- src/projectlist.cpp | 243 +++++++++++++++++++++---------- src/projectlist.h | 35 +++-- src/projectsettings.cpp | 4 +- src/renderer.cpp | 8 +- src/renderer.h | 3 + src/widgets/clipproperties_ui.ui | 35 +++-- src/widgets/configproject_ui.ui | 54 ++++--- 18 files changed, 386 insertions(+), 211 deletions(-) diff --git a/src/clipproperties.cpp b/src/clipproperties.cpp index 8364d1ad..3b595938 100644 --- a/src/clipproperties.cpp +++ b/src/clipproperties.cpp @@ -205,8 +205,34 @@ ClipProperties::ClipProperties(DocClipBase *clip, Timecode tc, double fps, QWidg if (props.contains("frequency")) QTreeWidgetItem *item = new QTreeWidgetItem(m_view.clip_aproperties, QStringList() << i18n("Frequency") << props.value("frequency")); + CLIPTYPE t = m_clip->clipType(); + + if (props.contains("proxy") && props.value("proxy") != "-") { + KFileItem f(KFileItem::Unknown, KFileItem::Unknown, KUrl(props.value("proxy")), true); + QFrame* line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + m_proxyContainer = new QFrame(); + m_proxyContainer->setFrameShape(QFrame::NoFrame); + QHBoxLayout *l = new QHBoxLayout; + l->addWidget(new QLabel(i18n("Proxy clip: %1").arg(KIO::convertSize(f.size())))); + l->addStretch(5); + QPushButton *pb = new QPushButton(i18n("Delete proxy")); + l->addWidget(pb); + connect(pb, SIGNAL(clicked()), this, SLOT(slotDeleteProxy())); + m_proxyContainer->setLayout(l); + if (t == AUDIO) { + m_view.tab_audio->layout()->addWidget(line); + m_view.tab_audio->layout()->addWidget(m_proxyContainer); + } + else { + m_view.tab_video->layout()->addWidget(line); + m_view.tab_video->layout()->addWidget(m_proxyContainer); + } + } + if (t != AUDIO && t != AV) { m_view.clip_force_aindex->setEnabled(false); } @@ -989,6 +1015,16 @@ void ClipProperties::slotUpdateDurationFormat(int ix) } } +void ClipProperties::slotDeleteProxy() +{ + QString proxy = m_clip->getProperty("proxy"); + QFile::remove(proxy); + QMap props; + props.insert("proxy", QString()); + emit applyNewClipProperties(m_clip->getId(), m_clip->properties(), props, false, true); + if (m_proxyContainer) delete m_proxyContainer; +} + #include "clipproperties.moc" diff --git a/src/clipproperties.h b/src/clipproperties.h index 982b1609..27d3ffad 100644 --- a/src/clipproperties.h +++ b/src/clipproperties.h @@ -68,6 +68,7 @@ private slots: void slotUpdateDurationFormat(int ix); void slotApplyProperties(); void slotModified(); + void slotDeleteProxy(); private: Ui::ClipProperties_UI m_view; @@ -81,6 +82,8 @@ private: bool m_clipNeedsRefresh; /** clip resource changed, reload it */ bool m_clipNeedsReLoad; + /** Frame with proxy info / delete button */ + QFrame* m_proxyContainer; signals: void addMarker(const QString &, GenTime, QString); diff --git a/src/definitions.h b/src/definitions.h index fd63c745..6b0576b0 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -58,6 +58,8 @@ enum MessageType { enum TRACKTYPE { AUDIOTRACK = 0, VIDEOTRACK = 1 }; +enum PROXYSTATUS { NOPROXY = 0, PROXYWAITING = 1, CREATINGPROXY = 2, PROXYDONE = 3, PROXYCRASHED = 4}; + struct TrackInfo { TRACKTYPE type; QString trackName; diff --git a/src/docclipbase.cpp b/src/docclipbase.cpp index a299a57d..50300cc4 100644 --- a/src/docclipbase.cpp +++ b/src/docclipbase.cpp @@ -53,8 +53,7 @@ DocClipBase::DocClipBase(ClipManager *clipManager, QDomElement xml, const QStrin m_audioThumbCreated(false), m_id(id), m_placeHolder(xml.hasAttribute("placeholder")), - m_properties(), - m_abortProxy(false) + m_properties() { int type = xml.attribute("type").toInt(); m_clipType = (CLIPTYPE) type; @@ -863,7 +862,11 @@ QString DocClipBase::getClipHash() const if (m_clipType == SLIDESHOW) hash = QCryptographicHash::hash(m_properties.value("resource").toAscii().data(), QCryptographicHash::Md5).toHex(); else if (m_clipType == COLOR) hash = QCryptographicHash::hash(m_properties.value("colour").toAscii().data(), QCryptographicHash::Md5).toHex(); else if (m_clipType == TEXT) hash = QCryptographicHash::hash(QString("title" + getId() + m_properties.value("xmldata")).toUtf8().data(), QCryptographicHash::Md5).toHex(); - else hash = m_properties.value("file_hash"); + else { + if (m_properties.contains("file_hash")) hash = m_properties.value("file_hash"); + else hash = getHash(fileURL().path()); + + } return hash; } @@ -967,6 +970,16 @@ void DocClipBase::setProperty(const QString &key, const QString &value) resetProducerProperty("set.force_full_luma"); } else setProducerProperty("set.force_full_luma", value.toInt()); } + else if (key == "proxy") { + // If value is "-", that means user manually disabled proxy on this clip + if (value.isEmpty() || value == "-") { + // reset proxy + emit abortProxy(m_id); + } + else { + emit createProxy(m_id); + } + } } QMap DocClipBase::properties() const @@ -1081,64 +1094,5 @@ bool DocClipBase::hasAudioCodec(const QString &codec) const return prod->get(property) == codec; } -void DocClipBase::generateProxy(KUrl proxyFolder, QString params) -{ - if (m_proxyThread.isRunning()) return; - QStringList parameters; - parameters << "-i" << m_properties.value("resource"); - foreach(QString s, params.split(' ')) - parameters << s; - // Make sure we don't block when proxy file already exists - parameters << "-y"; - if (m_properties.value("file_hash").isEmpty()) getFileHash(m_properties.value("resource")); - QString proxydir=proxyFolder.path( KUrl::AddTrailingSlash) + "proxy/"; - KStandardDirs::makeDir(proxydir); - QString path = proxydir + m_properties.value("file_hash") + ".avi"; - setProperty("proxy", path.toUtf8().data()); - if (QFile::exists(path)) { - emit proxyReady(m_id, true); - return; - } - parameters << path; - m_proxyThread = QtConcurrent::run(this, &DocClipBase::slotGenerateProxy, parameters); -} - -void DocClipBase::slotGenerateProxy(QStringList parameters) -{ - QProcess myProcess; - myProcess.start("ffmpeg", parameters); - myProcess.waitForStarted(); - int result = -1; - while (myProcess.state() != QProcess::NotRunning) { - // building proxy file - if (m_abortProxy) { - QString path = getProperty("proxy"); - myProcess.close(); - myProcess.waitForFinished(); - QFile::remove(path); - m_abortProxy = false; - result = 1; - } - myProcess.waitForFinished(500); - } - myProcess.waitForFinished(); - if (result == -1) result = myProcess.exitStatus(); - if (result == 0) emit proxyReady(m_id, true); - else { - clearProperty("proxy"); - emit proxyReady(m_id, false); - } -} -void DocClipBase::abortProxy() -{ - if (m_proxyThread.isRunning()) { - // Proxy is being created, abort - m_abortProxy = true; - } - else { - clearProperty("proxy"); - emit proxyReady(m_id, false); - } -} diff --git a/src/docclipbase.h b/src/docclipbase.h index a130e16a..1be1b0a4 100644 --- a/src/docclipbase.h +++ b/src/docclipbase.h @@ -204,10 +204,6 @@ Q_OBJECT public: bool hasAudioCodec(const QString &codec) const; bool checkHash() const; void setPlaceHolder(bool place); - /** @brief Generate a proxy clip (lower resolution copy) named like the clip's hash. */ - void generateProxy(KUrl proxyFolder, QString params); - /** @brief Abort creation of the proxy clip (lower resolution copy). */ - void abortProxy(); private: // Private attributes @@ -244,9 +240,6 @@ private: // Private attributes /** Holds clip metadata like author, copyright,... */ QMap m_metadata; - QFuture m_proxyThread; - /** Used to kill the proxy thread */ - bool m_abortProxy; /** Create connections for audio thumbnails */ void slotCreateAudioTimer(); void slotRefreshProducer(); @@ -273,12 +266,13 @@ public slots: QMap properties() const; QMap metadata() const; -private slots: - void slotGenerateProxy(QStringList parameters); signals: void gotAudioData(); - void proxyReady(const QString &, bool success); + /** @brief Generate a proxy clip (lower resolution copy) named like the clip's hash. */ + void createProxy(const QString id); + /** @brief Abort creation of the proxy clip (lower resolution copy). */ + void abortProxy(const QString id); }; #endif diff --git a/src/kdenlivedoc.cpp b/src/kdenlivedoc.cpp index d3c400bc..00425717 100644 --- a/src/kdenlivedoc.cpp +++ b/src/kdenlivedoc.cpp @@ -76,8 +76,8 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup m_documentProperties["zoneout"] = "100"; m_documentProperties["enableproxy"] = QString::number((int) KdenliveSettings::enableproxy()); m_documentProperties["proxyparams"] = KdenliveSettings::proxyparams(); - m_documentProperties["generateproxy"] = QString::number((int) KdenliveSettings::enableproxy()); - m_documentProperties["proxyminsize"] = "1000"; + m_documentProperties["generateproxy"] = QString::number((int) KdenliveSettings::generateproxy()); + m_documentProperties["proxyminsize"] = QString::number(KdenliveSettings::proxyminsize()); if (!url.isEmpty()) { QString tmpFile; diff --git a/src/kdenlivesettings.kcfg b/src/kdenlivesettings.kcfg index ce01fb02..dff10c95 100644 --- a/src/kdenlivesettings.kcfg +++ b/src/kdenlivesettings.kcfg @@ -92,6 +92,16 @@ false + + + false + + + + + 1000 + + -f avi -acodec libmp3lame -ac 2 -ab 92k -ar 48000 -vcodec mpeg2video -g 5 -deinterlace -s 480x270 -b 150k diff --git a/src/kdenlivesettingsdialog.cpp b/src/kdenlivesettingsdialog.cpp index f0a3f6cb..7e2e9169 100644 --- a/src/kdenlivesettingsdialog.cpp +++ b/src/kdenlivesettingsdialog.cpp @@ -64,6 +64,8 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap& map QWidget *p8 = new QWidget; m_configProject.setupUi(p8); m_page8 = addPage(p8, i18n("Project Defaults"), "document-new"); + connect(m_configProject.kcfg_enableproxy, SIGNAL(toggled(bool)), m_configProject.proxy_frame, SLOT(setEnabled(bool))); + m_configProject.proxy_frame->setEnabled(KdenliveSettings::enableproxy()); QWidget *p3 = new QWidget; m_configTimeline.setupUi(p3); @@ -620,6 +622,14 @@ void KdenliveSettingsDialog::updateSettings() if (m_configProject.kcfg_enableproxy->isChecked() != KdenliveSettings::enableproxy()) { KdenliveSettings::setEnableproxy(m_configProject.kcfg_enableproxy->isChecked()); } + + if (m_configProject.kcfg_generateproxy->isChecked() != KdenliveSettings::generateproxy()) { + KdenliveSettings::setGenerateproxy(m_configProject.kcfg_generateproxy->isChecked()); + } + + if (m_configProject.kcfg_proxyminsize->value() != KdenliveSettings::proxyminsize()) { + KdenliveSettings::setProxyminsize(m_configProject.kcfg_proxyminsize->value()); + } if (m_modified) { // The transcoding profiles were modified, save. diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9a3382f9..62344242 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -860,6 +860,7 @@ void MainWindow::slotConnectMonitors() 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_projectMonitor->render, SIGNAL(removeInvalidClip(const QString &, bool)), m_projectList, SLOT(slotRemoveInvalidClip(const QString &, bool))); + connect(m_projectMonitor->render, SIGNAL(removeInvalidProxy(const QString &)), m_projectList, SLOT(slotRemoveInvalidProxy(const QString &))); connect(m_clipMonitor, SIGNAL(refreshClipThumbnail(const QString &, bool)), m_projectList, SLOT(slotRefreshClipThumbnail(const QString &, bool))); @@ -1798,8 +1799,8 @@ void MainWindow::newFile(bool showProjectSettings, bool force) QPoint projectTracks(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks()); bool useProxy = KdenliveSettings::enableproxy(); QString proxyParams = KdenliveSettings::proxyparams(); - bool generateProxy = KdenliveSettings::enableproxy(); - int proxyMinSize = 1000; + bool generateProxy = KdenliveSettings::generateproxy(); + int proxyMinSize = KdenliveSettings::proxyminsize(); if (!showProjectSettings) { if (!KdenliveSettings::activatetabs()) if (!closeCurrentDocument()) @@ -2224,11 +2225,25 @@ void MainWindow::slotEditProjectSettings() if (KdenliveSettings::videothumbnails() != w->enableVideoThumbs()) slotSwitchVideoThumbs(); if (KdenliveSettings::audiothumbnails() != w->enableAudioThumbs()) slotSwitchAudioThumbs(); if (m_activeDocument->profilePath() != profile) slotUpdateProjectProfile(profile); - m_activeDocument->setDocumentProperty("proxyparams", w->proxyParams()); - m_activeDocument->setDocumentProperty("generateproxy", QString::number((int) w->generateProxy())); - m_activeDocument->setDocumentProperty("proxyminsize", QString::number(w->proxyMinSize())); + if (m_activeDocument->getDocumentProperty("proxyparams") != w->proxyParams()) { + m_activeDocument->setModified(); + m_activeDocument->setDocumentProperty("proxyparams", w->proxyParams()); + if (m_activeDocument->clipManager()->clipsCount() > 0 && KMessageBox::questionYesNo(this, i18n("You have changed the proxy parameters. Do you want to recreate all proxy clips for this project?")) == KMessageBox::Yes) { + //TODO: rebuild all proxies + //m_activeDocument->rebuildAllProxies(); + } + } + if (m_activeDocument->getDocumentProperty("generateproxy") != QString::number((int) w->generateProxy())) { + m_activeDocument->setModified(); + m_activeDocument->setDocumentProperty("generateproxy", QString::number((int) w->generateProxy())); + } + if (m_activeDocument->getDocumentProperty("proxyminsize") != QString::number(w->proxyMinSize())) { + m_activeDocument->setModified(); + m_activeDocument->setDocumentProperty("proxyminsize", QString::number(w->proxyMinSize())); + } if (QString::number((int) w->useProxy()) != m_activeDocument->getDocumentProperty("enableproxy")) { m_activeDocument->setDocumentProperty("enableproxy", QString::number((int) w->useProxy())); + m_activeDocument->setModified(); slotUpdateProxySettings(); } } @@ -3192,11 +3207,12 @@ void MainWindow::slotShowClipProperties(QList cliplist, QMaptimecode(), commonproperties, this); if (dia.exec() == QDialog::Accepted) { QUndoCommand *command = new QUndoCommand(); + command->setText(i18n("Edit clips")); QMap newImageProps = dia.properties(); // Transparency setting applies only for images QMap newProps = newImageProps; newProps.remove("transparency"); - command->setText(i18n("Edit clips")); + for (int i = 0; i < cliplist.count(); i++) { DocClipBase *clip = cliplist.at(i); if (clip->clipType() == IMAGE) diff --git a/src/projectitem.cpp b/src/projectitem.cpp index bf02065b..018655c9 100644 --- a/src/projectitem.cpp +++ b/src/projectitem.cpp @@ -32,17 +32,17 @@ const int ProxyRole = Qt::UserRole + 5; const int itemHeight = 38; ProjectItem::ProjectItem(QTreeWidget * parent, DocClipBase *clip) : + QTreeWidgetItem(parent, PROJECTCLIPTYPE), m_clip(clip), - m_clipId(clip->getId()), - QTreeWidgetItem(parent, PROJECTCLIPTYPE) + m_clipId(clip->getId()) { buildItem(); } ProjectItem::ProjectItem(QTreeWidgetItem * parent, DocClipBase *clip) : + QTreeWidgetItem(parent, PROJECTCLIPTYPE), m_clip(clip), - m_clipId(clip->getId()), - QTreeWidgetItem(parent, PROJECTCLIPTYPE) + m_clipId(clip->getId()) { buildItem(); @@ -230,21 +230,27 @@ void ProjectItem::setProperties(const QMap < QString, QString > &attributes, con } } -void ProjectItem::setProxyStatus(int status) +void ProjectItem::setProxyStatus(PROXYSTATUS status) { - if (status == data(0, ProxyRole).toInt()) return; setData(0, ProxyRole, status); - if (m_clip && status == 0) m_clip->abortProxy(); } bool ProjectItem::hasProxy() const { if (m_clip == NULL) return false; - return !m_clip->getProperty("proxy").isEmpty(); + if (m_clip->getProperty("proxy").isEmpty() || m_clip->getProperty("proxy") == "-" || data(0, ProxyRole).toInt() == PROXYCRASHED) return false; + return true; +} + +bool ProjectItem::isProxyReady() const +{ + return (data(0, ProxyRole).toInt() == PROXYDONE); } bool ProjectItem::isProxyRunning() const { - return (data(0, ProxyRole).toInt() == 1); + PROXYSTATUS s = (PROXYSTATUS) data(0, ProxyRole).toInt(); + if (s == PROXYWAITING || s == CREATINGPROXY) return true; + return false; } diff --git a/src/projectitem.h b/src/projectitem.h index c4e5b1bc..9a017f0c 100644 --- a/src/projectitem.h +++ b/src/projectitem.h @@ -64,9 +64,11 @@ public: static int itemDefaultHeight(); void slotSetToolTip(); /** \brief Set the status of proxy clip creation. 0 = no proxy, 1 = creating proxy, 2 = proxy created. */ - void setProxyStatus(int status); + void setProxyStatus(PROXYSTATUS status); /** \brief Returns the proxy status for this clip (true means there is a proxy clip). */ bool hasProxy() const; + /** \brief Returns true if the proxy for this clip is ready. */ + bool isProxyReady() const; /** \brief Returns true if we are currently creating the proxy for this clip. */ bool isProxyRunning() const; @@ -79,8 +81,8 @@ public: private: CLIPTYPE m_clipType; - QString m_clipId; DocClipBase *m_clip; + QString m_clipId; void buildItem(); diff --git a/src/projectlist.cpp b/src/projectlist.cpp index e6d91498..87870c64 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -49,6 +49,8 @@ #include #include #include +#include + #ifdef NEPOMUK #include #include @@ -745,11 +747,13 @@ void ProjectList::slotItemEdited(QTreeWidgetItem *item, int column) QMap oldprops; QMap newprops; oldprops["name"] = clip->referencedClip()->getProperty("name"); - newprops["name"] = item->text(0); - slotUpdateClipProperties(clip, newprops); - emit projectModified(); - EditClipCommand *command = new EditClipCommand(this, clip->clipId(), oldprops, newprops, false); - m_commandStack->push(command); + if (oldprops.value("name") != item->text(0)) { + newprops["name"] = item->text(0); + slotUpdateClipProperties(clip, newprops); + emit projectModified(); + EditClipCommand *command = new EditClipCommand(this, clip->clipId(), oldprops, newprops, false); + m_commandStack->push(command); + } } } } @@ -989,39 +993,17 @@ 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")); + connect(clip, SIGNAL(createProxy(const QString)), this, SLOT(slotCreateProxy(const QString))); + connect(clip, SIGNAL(abortProxy(const QString)), this, SLOT(slotAbortProxy(const QString))); if (getProperties) { m_listView->processLayout(); m_refreshed = false; - // Proxy clips - CLIPTYPE t = clip->clipType(); - if ((t == VIDEO || t == AV || t == UNKNOWN) && useProxy()) { - if (clip->getProperty("proxy").isEmpty()) { - - //connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool))); - //setProxyStatus(item, 1); - //clip->generateProxy(m_doc->projectFolder(), proxyParams()); - } - else { - // Proxy clip already created - setProxyStatus(item, 2); - QDomElement e = clip->toXML().cloneNode().toElement(); - e.removeAttribute("file_hash"); - m_infoQueue.insert(clip->getId(), e); - } - } - //else { - // We don't use proxies - // remove file_hash so that we load all properties for the clip - QDomElement e = clip->toXML().cloneNode().toElement(); - e.removeAttribute("file_hash"); - m_infoQueue.insert(clip->getId(), e); - //} - //m_render->getFileProperties(clip->toXML(), clip->getId(), true); + QDomElement e = clip->toXML().cloneNode().toElement(); + e.removeAttribute("file_hash"); + m_infoQueue.insert(clip->getId(), e); } - else if (!clip->getProperty("proxy").isEmpty()) { - connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool))); - setProxyStatus(item, 1); - clip->generateProxy(m_doc->projectFolder(), proxyParams()); + else if (item->hasProxy() && !item->isProxyRunning()) { + slotCreateProxy(clip->getId()); } clip->askForAudioThumbs(); @@ -1069,21 +1051,16 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue); } -void ProjectList::slotGotProxy(const QString &id, bool success) +void ProjectList::slotGotProxy(const QString &id) { ProjectItem *item = getItemById(id); if (item) { - if (success) { - // Proxy clip successfully created - setProxyStatus(item, 2); - QDomElement e = item->referencedClip()->toXML().cloneNode().toElement(); - e.removeAttribute("file_hash"); - e.setAttribute("replace", 1); - m_infoQueue.insert(id, e); - if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue); - } - else setProxyStatus(item, 0); - update(); + // Proxy clip successfully created + QDomElement e = item->referencedClip()->toXML().cloneNode().toElement(); + //e.removeAttribute("file_hash"); + e.setAttribute("replace", 1); + m_infoQueue.insert(id, e); + if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue); } } @@ -1309,6 +1286,23 @@ void ProjectList::slotRemoveInvalidClip(const QString &id, bool replace) } } +void ProjectList::slotRemoveInvalidProxy(const QString &id) +{ + ProjectItem *item = getItemById(id); + if (item) { + item->setProxyStatus(PROXYCRASHED); + QString path = item->referencedClip()->getProperty("proxy"); + KUrl proxyFolder(m_doc->projectFolder().path( KUrl::AddTrailingSlash) + "proxy/"); + + //Security check: make sure the invalid proxy file is in the proxy folder + if (proxyFolder.isParentOf(KUrl(path))) { + QFile::remove(path); + } + } + m_processingClips.removeAll(id); + if (!m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue); +} + void ProjectList::slotAddColorClip() { if (!m_commandStack) @@ -1426,6 +1420,7 @@ void ProjectList::setDocument(KdenliveDoc *doc) m_timecode = doc->timecode(); m_commandStack = doc->commandStack(); m_doc = doc; + m_proxyList.clear(); QMap flist = doc->clipManager()->documentFolderList(); QMapIterator f(flist); @@ -1598,16 +1593,19 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled); toReload = clipId; } - if (!useProxy() && item->referencedClip()->getProperty("proxy").isEmpty()) setProxyStatus(item, 0); + + // Proxy stuff QString size = properties.value("frame_size"); DocClipBase *clip = item->referencedClip(); - if (useProxy() && (item->clipType() == AV || item->clipType() == VIDEO) && generateProxy() && size.section('x', 0, 0).toInt() > proxyMinSize()) { + if (!useProxy() && item->referencedClip()->getProperty("proxy").isEmpty()) setProxyStatus(item, NOPROXY); + if (useProxy() && generateProxy() && item->referencedClip()->getProperty("proxy") == "-") setProxyStatus(item, NOPROXY); + else if (useProxy() && !item->isProxyRunning() && (item->clipType() == AV || item->clipType() == VIDEO) && generateProxy() && size.section('x', 0, 0).toInt() > proxyMinSize()) { if (clip->getProperty("proxy").isEmpty()) { - connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool))); - setProxyStatus(item, 1); - clip->generateProxy(m_doc->projectFolder(), proxyParams()); + QString proxydir = m_doc->projectFolder().path( KUrl::AddTrailingSlash) + "proxy/"; + clip->setProperty("proxy", proxydir + item->referencedClip()->getClipHash() + ".avi"); } } + clip->setProducer(producer, replace); clip->askForAudioThumbs(); if (!replace && item->data(0, Qt::DecorationRole).isNull()) @@ -2059,14 +2057,105 @@ QMap ProjectList::getProxies() } item = static_cast(*it); if (item && item->referencedClip() != NULL) { - QString proxy = item->referencedClip()->getProperty("proxy"); - if (!proxy.isEmpty()) list.insert(proxy, item->clipUrl().path()); + if (item->hasProxy()) { + QString proxy = item->referencedClip()->getProperty("proxy"); + list.insert(proxy, item->clipUrl().path()); + } } ++it; } return list; } +void ProjectList::slotCreateProxy(const QString id) +{ + ProjectItem *item = getItemById(id); + if (!item || item->isProxyRunning()) return; + setProxyStatus(id, PROXYWAITING); + if (m_abortProxyId.contains(id)) m_abortProxyId.removeAll(id); + QtConcurrent::run(this, &ProjectList::slotGenerateProxy, id); +} + +void ProjectList::slotAbortProxy(const QString id) +{ + if (m_proxyList.contains(id)) m_proxyList.removeAll(id); + ProjectItem *item = getItemById(id); + if (item) { + if (item->isProxyReady()) slotGotProxy(id); + else if (item->isProxyRunning()) m_abortProxyId << id; + setProxyStatus(id, NOPROXY); + } +} + +void ProjectList::slotGenerateProxy(const QString id) +{ + setProxyStatus(id, CREATINGPROXY); + ProjectItem *item = getItemById(id); + if (item == NULL) return; + QString path = item->referencedClip()->getProperty("proxy"); + if (path.isEmpty()) { + setProxyStatus(id, PROXYCRASHED); + return; + } + + QString url = item->clipUrl().path(); + + if (QFile::exists(path)) { + setProxyStatus(id, PROXYDONE); + slotGotProxy(id); + return; + } + else { + // Make sure proxy path is writable + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + setProxyStatus(id, PROXYCRASHED); + return; + } + file.close(); + QFile::remove(path); + } + + QStringList parameters; + parameters << "-i" << url; + QString params = proxyParams(); + foreach(QString s, params.split(' ')) + parameters << s; + + // Make sure we don't block when proxy file already exists + parameters << "-y"; + parameters << path; + QProcess myProcess; + myProcess.start("ffmpeg", parameters); + myProcess.waitForStarted(); + int result = -1; + while (myProcess.state() != QProcess::NotRunning) { + // building proxy file + if (m_abortProxyId.contains(id)) { + myProcess.close(); + myProcess.waitForFinished(); + m_abortProxyId.removeAll(id); + QFile::remove(path); + setProxyStatus(id, NOPROXY); + result = -2; + + } + myProcess.waitForFinished(500); + } + myProcess.waitForFinished(); + if (result == -1) result = myProcess.exitStatus(); + if (result == 0) { + // proxy successfully created + setProxyStatus(id, PROXYDONE); + slotGotProxy(id); + } + else if (result == 1) { + // Proxy process crashed + QFile::remove(path); + setProxyStatus(id, PROXYCRASHED); + } +} + void ProjectList::updateProxyConfig() { ProjectItem *item; @@ -2086,12 +2175,12 @@ void ProjectList::updateProxyConfig() if (generateProxy() && useProxy()) { DocClipBase *clip = item->referencedClip(); if (clip->getProperty("frame_size").section('x', 0, 0).toInt() > proxyMinSize()) { - connect(clip, SIGNAL(proxyReady(const QString &, bool)), this, SLOT(slotGotProxy(const QString &, bool))); - setProxyStatus(item, 1); - clip->generateProxy(m_doc->projectFolder(), proxyParams()); + kDebug()<<"// UPDATE ST PROXY"; + slotCreateProxy(clip->getId()); + setProxyStatus(item, PROXYWAITING); } } - else if (!item->referencedClip()->getProperty("proxy").isEmpty()) { + else if (!item->hasProxy()) { // remove proxy item->referencedClip()->clearProperty("proxy"); QDomElement e = item->toXml().cloneNode().toElement(); @@ -2109,6 +2198,15 @@ void ProjectList::slotProxyCurrentItem(bool doProxy) { QList list = m_listView->selectedItems(); QTreeWidgetItem *listItem; + QUndoCommand *command = new QUndoCommand(); + command->setText(i18np("Edit clip", "Edit clips", list.count())); + + // Make sure the proxy folder exists + QString proxydir = m_doc->projectFolder().path( KUrl::AddTrailingSlash) + "proxy/"; + KStandardDirs::makeDir(proxydir); + + QMap newProps; + if (!doProxy) newProps.insert("proxy", "-"); for (int i = 0; i < list.count(); i++) { listItem = list.at(i); if (listItem->type() == PROJECTFOLDERTYPE) { @@ -2122,30 +2220,27 @@ void ProjectList::slotProxyCurrentItem(bool doProxy) CLIPTYPE t = item->clipType(); if ((t == VIDEO || t == AV || t == UNKNOWN) && item->referencedClip()) { if (doProxy) { - DocClipBase *clip = item->referencedClip(); - connect(clip, SIGNAL(proxyReady(const QString&, bool)), this, SLOT(slotGotProxy(const QString&, bool))); - setProxyStatus(item, 1); - clip->generateProxy(m_doc->projectFolder(), proxyParams()); - } - else if (!item->referencedClip()->getProperty("proxy").isEmpty()) { - // remove proxy - if (!item->isProxyRunning()) { - setProxyStatus(item, 0); - QDomElement e = item->toXml().cloneNode().toElement(); - e.removeAttribute("file_hash"); - e.setAttribute("replace", 1); - m_infoQueue.insert(item->clipId(), e); - } - else setProxyStatus(item, 0); + newProps.clear(); + QString path = proxydir + item->referencedClip()->getClipHash() + ".avi"; + newProps.insert("proxy", path); } + new EditClipCommand(this, item->clipId(), item->referencedClip()->properties(), newProps, true, command); } } } - if (!m_infoQueue.isEmpty() && !m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue); + m_doc->commandStack()->push(command); + //if (!m_infoQueue.isEmpty() && !m_queueRunner.isRunning() && m_processingClips.isEmpty()) m_queueRunner = QtConcurrent::run(this, &ProjectList::slotProcessNextClipInQueue); +} + +void ProjectList::setProxyStatus(const QString id, PROXYSTATUS status) +{ + ProjectItem *item = getItemById(id); + setProxyStatus(item, status); } -void ProjectList::setProxyStatus(ProjectItem *item, int status) +void ProjectList::setProxyStatus(ProjectItem *item, PROXYSTATUS status) { + if (item == NULL) return; monitorItemEditing(false); item->setProxyStatus(status); monitorItemEditing(true); diff --git a/src/projectlist.h b/src/projectlist.h index fb08f89e..266775f7 100644 --- a/src/projectlist.h +++ b/src/projectlist.h @@ -117,16 +117,19 @@ public: QString proxyText; QBrush brush; QColor color; - if (proxy == 1) { - proxyText = i18n("Generating proxy..."); - brush = option.palette.highlight(); - color = option.palette.color(QPalette::HighlightedText); - } - else { + if (proxy == PROXYDONE) { proxyText = i18n("Proxy"); brush = option.palette.mid(); color = option.palette.color(QPalette::WindowText); } + else { + if (proxy == CREATINGPROXY) proxyText = i18n("Generating proxy..."); + else if (proxy == PROXYWAITING) proxyText = i18n("Waiting proxy..."); + else if (proxy == PROXYCRASHED) proxyText = i18n("proxy crashed"); + brush = option.palette.highlight(); + color = option.palette.color(QPalette::HighlightedText); + } + txtBounding = painter->boundingRect(r2, Qt::AlignRight | Qt::AlignVCenter, " " + proxyText + " "); painter->setPen(Qt::NoPen); painter->setBrush(brush); @@ -212,6 +215,7 @@ public slots: void slotRefreshClipThumbnail(const QString &clipId, bool update = true); void slotRefreshClipThumbnail(QTreeWidgetItem *item, bool update = true); void slotRemoveInvalidClip(const QString &id, bool replace); + void slotRemoveInvalidProxy(const QString &id); void slotSelectClip(const QString &ix); /** @brief Prepares removing the selected items. */ @@ -260,6 +264,11 @@ private: QList m_thumbnailQueue; QAction *m_proxyAction; QStringList m_processingClips; + /** @brief Holds a list of ids for the clips that need to be proxied. */ + QStringList m_proxyList; + /** @brief Holds a list of proxy clip that should be aborted. */ + QStringList m_abortProxyId; + void requestClipThumbnail(const QString id); /** @brief Creates an EditFolderCommand to change the name of an folder item. */ @@ -280,8 +289,10 @@ private: /** @brief Set the Proxy status on a clip. * @param item The clip item to set status - * @param status The status (1 = creating proxy, 2 = proxy is ok) */ - void setProxyStatus(ProjectItem *item, int status); + * @param status The proxy status (see definitions.h) */ + void setProxyStatus(const QString id, PROXYSTATUS status); + void setProxyStatus(ProjectItem *item, PROXYSTATUS status); + void monitorItemEditing(bool enable); private slots: @@ -314,9 +325,15 @@ private slots: /** @brief Add a sequence from the stopmotion widget. */ void slotAddOrUpdateSequence(const QString frameName); /** @brief A proxy clip was created, update display. */ - void slotGotProxy(const QString &id, bool success); + void slotGotProxy(const QString &id); /** @brief Enable / disable proxy for current clip. */ void slotProxyCurrentItem(bool doProxy); + /** @brief Put clip in the proxy waiting list. */ + void slotCreateProxy(const QString id); + /** @brief Stop creation of this clip's proxy. */ + void slotAbortProxy(const QString id); + /** @brief Start creation of proxy clip. */ + void slotGenerateProxy(const QString id); signals: void clipSelected(DocClipBase *, QPoint zone = QPoint()); diff --git a/src/projectsettings.cpp b/src/projectsettings.cpp index f4ca1a0c..c4e8e4d1 100644 --- a/src/projectsettings.cpp +++ b/src/projectsettings.cpp @@ -77,8 +77,8 @@ ProjectSettings::ProjectSettings(ProjectList *projectlist, QStringList lumas, in } else { enable_proxy->setChecked(KdenliveSettings::enableproxy()); - generate_proxy->setChecked(KdenliveSettings::enableproxy()); - proxy_minsize->setValue(1000); + generate_proxy->setChecked(KdenliveSettings::generateproxy()); + proxy_minsize->setValue(KdenliveSettings::proxyminsize()); proxy_params->setText(KdenliveSettings::proxyparams()); proxy_box->setEnabled(KdenliveSettings::enableproxy()); } diff --git a/src/renderer.cpp b/src/renderer.cpp index 48b25a29..55ad1a0f 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -538,7 +538,7 @@ void Render::slotSplitView(bool doit) void Render::getFileProperties(const QDomElement xml, const QString &clipId, int imageHeight, bool replaceProducer, bool selectClip) { QString path; - if (xml.hasAttribute("proxy")) path = xml.attribute("proxy"); + if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") path = xml.attribute("proxy"); else path = xml.attribute("resource"); KUrl url = KUrl(path); Mlt::Producer *producer = NULL; @@ -569,7 +569,11 @@ void Render::getFileProperties(const QDomElement xml, const QString &clipId, int if (producer == NULL || producer->is_blank() || !producer->is_valid()) { kDebug() << " / / / / / / / / ERROR / / / / // CANNOT LOAD PRODUCER: "; - emit removeInvalidClip(clipId, replaceProducer); + if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") { + // Proxy file is corrupted + emit removeInvalidProxy(clipId); + } + else emit removeInvalidClip(clipId, replaceProducer); delete producer; return; } diff --git a/src/renderer.h b/src/renderer.h index e8716dd0..d8a54a23 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -350,7 +350,10 @@ signals: void durationChanged(int); void rendererPosition(int); void rendererStopped(int); + /** @brief The clip is not valid, should be removed from project. */ void removeInvalidClip(const QString &, bool replaceProducer); + /** @brief The proxy is not valid, should be deleted. */ + void removeInvalidProxy(const QString &); void refreshDocumentProducers(); /** @brief A frame's image has to be shown. diff --git a/src/widgets/clipproperties_ui.ui b/src/widgets/clipproperties_ui.ui index 8c1a43d5..3e46bf1f 100644 --- a/src/widgets/clipproperties_ui.ui +++ b/src/widgets/clipproperties_ui.ui @@ -14,7 +14,7 @@ Clip Properties - + Qt::Vertical @@ -34,52 +34,52 @@ - + true - + Description - + - + Duration - + 99:99:99:99; - + Size: - + File size - + Qt::Horizontal @@ -99,7 +99,7 @@ - + QTabWidget::South @@ -111,8 +111,8 @@ Video - - + + QFrame::NoFrame @@ -156,8 +156,8 @@ Audio - - + + QFrame::NoFrame @@ -790,6 +790,13 @@ + + + + Qt::Horizontal + + + diff --git a/src/widgets/configproject_ui.ui b/src/widgets/configproject_ui.ui index 52544b57..51b0eae1 100644 --- a/src/widgets/configproject_ui.ui +++ b/src/widgets/configproject_ui.ui @@ -6,8 +6,8 @@ 0 0 - 264 - 304 + 312 + 294 @@ -140,18 +140,41 @@ - - - Proxy clips + + + Enable proxy clips - - false + + + + + + QFrame::NoFrame + + + QFrame::Raised + + 0 + - + - Generate proxy clips + Generate proxy if clip larger than + + + + + + + pixels + + + 10000 + + + 1000 @@ -162,20 +185,13 @@ - - - - - 0 - 0 - - - + + - + Qt::Vertical -- 2.39.2