From: Jean-Baptiste Mardelle Date: Mon, 17 Jan 2011 23:58:55 +0000 (+0000) Subject: First step for implementation of proxy editing (for testing purpose only) X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=12c52e2d2e97f703714b83dfd385db2de5f03f18;p=kdenlive First step for implementation of proxy editing (for testing purpose only) svn path=/trunk/kdenlive/; revision=5329 --- diff --git a/src/docclipbase.cpp b/src/docclipbase.cpp index 485fe907..df2a4ec9 100644 --- a/src/docclipbase.cpp +++ b/src/docclipbase.cpp @@ -34,6 +34,7 @@ #include #include +#include #include @@ -1079,3 +1080,34 @@ bool DocClipBase::hasAudioCodec(const QString &codec) const return prod->get(property) == codec; } +void DocClipBase::generateProxy(KUrl proxyFolder) +{ + if (m_proxyThread.isRunning()) return; + QStringList parameters; + parameters << "-i" << m_properties.value("resource"); + QString params = KdenliveSettings::proxyparams().simplified(); + 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 path = proxyFolder.path(KUrl::AddTrailingSlash) + "proxy/" + 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) +{ + int result = QProcess::execute("ffmpeg", parameters); + if (result == 0) emit proxyReady(m_id, true); + else { + resetProducerProperty("proxy"); + emit proxyReady(m_id, false); + } +} + diff --git a/src/docclipbase.h b/src/docclipbase.h index 60e1402b..a17d554f 100644 --- a/src/docclipbase.h +++ b/src/docclipbase.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include @@ -202,6 +204,8 @@ 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); private: // Private attributes @@ -231,6 +235,8 @@ private: // Private attributes bool m_placeHolder; QList m_cutZones; + + QFuture m_proxyThread; void setAudioThumbCreated(bool isDone); /** Holds clip infos like fps, size,... */ @@ -263,9 +269,12 @@ public slots: QMap properties() const; QMap metadata() const; +private slots: + void slotGenerateProxy(QStringList parameters); signals: void gotAudioData(); + void proxyReady(const QString, bool success); }; #endif diff --git a/src/kdenlivesettings.kcfg b/src/kdenlivesettings.kcfg index f5d83b06..904d1162 100644 --- a/src/kdenlivesettings.kcfg +++ b/src/kdenlivesettings.kcfg @@ -78,9 +78,25 @@ - + + + + + false + + + + + true + + + + + -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 d2ba47fa..dcf15cf8 100644 --- a/src/kdenlivesettingsdialog.cpp +++ b/src/kdenlivesettingsdialog.cpp @@ -614,6 +614,10 @@ void KdenliveSettingsDialog::updateSettings() KdenliveSettings::setVolume(m_configSdl.kcfg_volume->value()); resetProfile = true; } + + if (m_configProject.kcfg_enableproxy->isChecked() != KdenliveSettings::enableproxy()) { + emit updateProxySettings(); + } if (m_modified) { // The transcoding profiles were modified, save. diff --git a/src/kdenlivesettingsdialog.h b/src/kdenlivesettingsdialog.h index 1f06efc7..6ffa99b9 100644 --- a/src/kdenlivesettingsdialog.h +++ b/src/kdenlivesettingsdialog.h @@ -104,6 +104,7 @@ signals: void customChanged(); void doResetProfile(); void updateCaptureFolder(); + void updateProxySettings(); }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1d3240c9..84859d96 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2572,7 +2572,7 @@ void MainWindow::slotPreferences(int page, int option) KdenliveSettingsDialog* dialog = new KdenliveSettingsDialog(actions, this); connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(updateConfiguration())); - //connect(dialog, SIGNAL(doResetProfile()), this, SLOT(slotDetectAudioDriver())); + connect(dialog, SIGNAL(updateProxySettings()), this, SLOT(slotUpdateProxySettings())); connect(dialog, SIGNAL(doResetProfile()), m_monitorManager, SLOT(slotResetProfiles())); #ifndef Q_WS_MAC connect(dialog, SIGNAL(updateCaptureFolder()), this, SLOT(slotUpdateCaptureFolder())); @@ -3740,16 +3740,14 @@ void MainWindow::slotPrepareRendering(bool scriptExport, bool zoneOnly, const QS return; } playlistPath = scriptPath + ".mlt"; - m_projectMonitor->saveSceneList(playlistPath); } else { KTemporaryFile temp; temp.setAutoRemove(false); temp.setSuffix(".mlt"); temp.open(); playlistPath = temp.fileName(); - m_projectMonitor->saveSceneList(playlistPath); } - + QString playlistContent = m_projectMonitor->sceneList(); if (!chapterFile.isEmpty()) { int in = 0; int out; @@ -3804,6 +3802,32 @@ void MainWindow::slotPrepareRendering(bool scriptExport, bool zoneOnly, const QS if (m_renderWidget->automaticAudioExport()) { exportAudio = m_activeTimeline->checkProjectAudio(); } else exportAudio = m_renderWidget->selectedAudioExport(); + + // Do we want proxy rendering + if (KdenliveSettings::enableproxy() && !m_renderWidget->proxyRendering()) { + // replace proxy clips with originals + QMap proxies = m_projectList->getProxies(); + QMapIterator i(proxies); + while (i.hasNext()) { + i.next(); + // Replace all keys with their values (proxy path with original path) + playlistContent.replace(i.key(), i.value()); + } + } + + // Do save scenelist + QFile file(playlistPath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + m_messageLabel->setMessage(i18n("Cannot write to file %1").arg(playlistPath), ErrorMessage); + return; + } + file.write(playlistContent.toUtf8()); + if (file.error() != QFile::NoError) { + m_messageLabel->setMessage(i18n("Cannot write to file %1").arg(playlistPath), ErrorMessage); + file.close(); + return; + } + file.close(); m_renderWidget->slotExport(scriptExport, m_activeTimeline->inPoint(), m_activeTimeline->outPoint(), playlistPath, scriptPath, exportAudio); } @@ -4112,6 +4136,12 @@ void MainWindow::slotDeleteClip(const QString &id) m_projectList->slotDeleteClip(id); } +void MainWindow::slotUpdateProxySettings() +{ + if (m_renderWidget) m_renderWidget->updateProxyConfig(); + //TODO: update proxy in project tree +} + #include "mainwindow.moc" #ifdef DEBUG_MAINW diff --git a/src/mainwindow.h b/src/mainwindow.h index 1e2e3ab5..34e24d1a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -534,6 +534,8 @@ private slots: void slotOpenStopmotion(); /** @brief Implements all the actions that are int he ActionsCollection. */ void slotDoAction(const QString& action_name); + /** @brief Update project because the use of proxy clips was enabled / disabled. */ + void slotUpdateProxySettings(); signals: Q_SCRIPTABLE void abortRenderJob(const QString &url); diff --git a/src/projectitem.cpp b/src/projectitem.cpp index 1f26d0f7..ac2a5256 100644 --- a/src/projectitem.cpp +++ b/src/projectitem.cpp @@ -28,6 +28,7 @@ #include const int DurationRole = Qt::UserRole + 1; +const int ProxyRole = Qt::UserRole + 5; const int itemHeight = 38; ProjectItem::ProjectItem(QTreeWidget * parent, DocClipBase *clip) : @@ -238,3 +239,8 @@ void ProjectItem::setProperties(const QMap < QString, QString > &attributes, con } } +void ProjectItem::setProxyStatus(int status) +{ + setData(0, ProxyRole, status); +} + diff --git a/src/projectitem.h b/src/projectitem.h index 0ed28021..06ff6979 100644 --- a/src/projectitem.h +++ b/src/projectitem.h @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -62,6 +63,8 @@ public: QString getClipHash() const; 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); virtual bool operator<(const QTreeWidgetItem &other)const { int column = treeWidget()->sortColumn(); @@ -74,6 +77,7 @@ private: CLIPTYPE m_clipType; QString m_clipId; DocClipBase *m_clip; + }; #endif diff --git a/src/projectlist.cpp b/src/projectlist.cpp index ff3b6acc..b2837a20 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -913,16 +913,7 @@ void ProjectList::deleteProjectFolder(QMap map) void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) { m_listView->setEnabled(false); - if (getProperties) { - m_listView->blockSignals(true); - m_refreshed = false; - // 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); - } - clip->askForAudioThumbs(); + if (getProperties) m_listView->blockSignals(true); const QString parent = clip->getProperty("groupid"); ProjectItem *item = NULL; if (!parent.isEmpty()) { @@ -941,8 +932,40 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) } if (item == NULL) item = new ProjectItem(m_listView, clip); + if (item->data(0, DurationRole).isNull()) item->setData(0, DurationRole, i18n("Loading")); + if (getProperties) { + m_listView->blockSignals(true); + m_refreshed = false; + + // Proxy clips + CLIPTYPE t = clip->clipType(); + if ((t == VIDEO || t == AV || t == UNKNOWN) && KdenliveSettings::enableproxy()) { + if (clip->getProperty("proxy").isEmpty()) { + connect(clip, SIGNAL(proxyReady(const QString, bool)), this, SLOT(slotGotProxy(const QString, bool))); + item->setProxyStatus(1); + clip->generateProxy(m_doc->projectFolder()); + } + else { + // Proxy clip already created + item->setProxyStatus(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); + } + clip->askForAudioThumbs(); + KUrl url = clip->fileURL(); - if (getProperties == false && !clip->getClipHash().isEmpty()) { QString cachedPixmap = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "thumbs/" + clip->getClipHash() + ".png"; if (QFile::exists(cachedPixmap)) { @@ -982,10 +1005,30 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) if (getProperties) m_listView->blockSignals(false); } + if (getProperties && !m_queueTimer.isActive()) slotProcessNextClipInQueue(); } +void ProjectList::slotGotProxy(const QString id, bool success) +{ + ProjectItem *item = getItemById(id); + if (item) { + disconnect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int))); + if (success) { + // Proxy clip successfully created + item->setProxyStatus(2); + QDomElement e = item->referencedClip()->toXML().cloneNode().toElement(); + e.removeAttribute("file_hash"); + m_infoQueue.insert(id, e); + if (!m_queueTimer.isActive()) slotProcessNextClipInQueue(); + } + else item->setProxyStatus(0); + connect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int))); + update(); + } +} + void ProjectList::slotResetProjectList() { m_listView->clear(); @@ -1366,7 +1409,6 @@ void ProjectList::slotCheckForEmptyQueue() void ProjectList::reloadClipThumbnails() { - kDebug() << "////////////// RELOAD CLIPS THUMBNAILS!!!"; m_thumbnailQueue.clear(); QTreeWidgetItemIterator it(m_listView); while (*it) { @@ -1889,4 +1931,25 @@ void ProjectList::slotAddOrUpdateSequence(const QString frameName) } else emit displayMessage(i18n("Sequence not found"), -2); } +QMap ProjectList::getProxies() +{ + QMap list; + ProjectItem *item; + QTreeWidgetItemIterator it(m_listView); + while (*it) { + if ((*it)->type() != PROJECTCLIPTYPE) { + // subitem + ++it; + continue; + } + item = static_cast(*it); + if (item && item->referencedClip() != NULL) { + QString proxy = item->referencedClip()->getProperty("proxy"); + if (!proxy.isEmpty()) list.insert(proxy, item->clipUrl().path()); + } + ++it; + } + return list; +} + #include "projectlist.moc" diff --git a/src/projectlist.h b/src/projectlist.h index 00ab5263..186ce3a2 100644 --- a/src/projectlist.h +++ b/src/projectlist.h @@ -107,7 +107,33 @@ public: int usage = index.data(UsageRole).toInt(); if (usage != 0) subText.append(QString(" (%1)").arg(usage)); if (option.state & (QStyle::State_Selected)) painter->setPen(option.palette.color(QPalette::Mid)); - painter->drawText(r2, Qt::AlignLeft | Qt::AlignVCenter , subText); + QRectF bounding; + painter->drawText(r2, Qt::AlignLeft | Qt::AlignVCenter , subText, &bounding); + + int proxy = index.data(Qt::UserRole + 5).toInt(); + if (proxy > 0) { + QRectF txtBounding; + QString proxyText; + QBrush brush; + QColor color; + if (proxy == 1) { + proxyText = i18n("Generating proxy..."); + brush = option.palette.highlight(); + color = option.palette.color(QPalette::HighlightedText); + } + else { + proxyText = i18n("Proxy"); + brush = option.palette.mid(); + color = option.palette.color(QPalette::WindowText); + } + txtBounding = painter->boundingRect(r2, Qt::AlignRight | Qt::AlignVCenter, " " + proxyText + " "); + painter->setPen(Qt::NoPen); + painter->setBrush(brush); + painter->drawRoundedRect(txtBounding, 2, 2); + painter->setPen(option.palette.highlightedText().color()); + painter->drawText(txtBounding, Qt::AlignHCenter | Qt::AlignVCenter , proxyText); + } + painter->restore(); } else if (index.column() == 2 && KdenliveSettings::activate_nepomuk()) { if (index.data().toString().isEmpty()) { @@ -161,6 +187,8 @@ public: /** @brief Returns a string list of all supported mime extensions. */ static QString getExtensions(); + /** @brief Returns a list of urls containing original and proxy urls. */ + QMap getProxies(); public slots: void setDocument(KdenliveDoc *doc); @@ -264,7 +292,8 @@ private slots: bool adjustProjectProfileToItem(ProjectItem *item = NULL); /** @brief Add a sequence from the stopmotion widget. */ void slotAddOrUpdateSequence(const QString frameName); - //void slotShowMenu(const QPoint &pos); + /** @brief A proxy clip was created, update display. */ + void slotGotProxy(const QString id, bool success); signals: void clipSelected(DocClipBase *, QPoint zone = QPoint()); @@ -286,3 +315,4 @@ signals: }; #endif + diff --git a/src/renderer.cpp b/src/renderer.cpp index 37316771..22873c4d 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -522,10 +522,12 @@ void Render::slotSplitView(bool doit) void Render::getFileProperties(const QDomElement xml, const QString &clipId, int imageHeight, bool replaceProducer) { - KUrl url = KUrl(xml.attribute("resource", QString())); + QString path; + if (KdenliveSettings::enableproxy() && xml.hasAttribute("proxy")) path = xml.attribute("proxy"); + else path = xml.attribute("resource"); + KUrl url = KUrl(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())) { emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer); diff --git a/src/renderwidget.cpp b/src/renderwidget.cpp index 5dc6e209..2535ced8 100644 --- a/src/renderwidget.cpp +++ b/src/renderwidget.cpp @@ -86,6 +86,8 @@ RenderWidget::RenderWidget(const QString &projectfolder, QWidget * parent) : if (KdenliveSettings::showrenderparams()) { m_view.buttonInfo->setDown(true); } else m_view.advanced_params->hide(); + + m_view.proxy_render->setHidden(!KdenliveSettings::enableproxy()); m_view.rescale_keep->setChecked(KdenliveSettings::rescalekeepratio()); connect(m_view.rescale_width, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateRescaleWidth(int))); @@ -1901,3 +1903,12 @@ bool RenderWidget::selectedAudioExport() const return (m_view.export_audio->checkState() != Qt::Unchecked); } +void RenderWidget::updateProxyConfig() +{ + m_view.proxy_render->setHidden(!KdenliveSettings::enableproxy()); +} + +bool RenderWidget::proxyRendering() +{ + return m_view.proxy_render->isChecked(); +} diff --git a/src/renderwidget.h b/src/renderwidget.h index f8958a67..7b1c20d8 100644 --- a/src/renderwidget.h +++ b/src/renderwidget.h @@ -127,6 +127,10 @@ public: bool automaticAudioExport() const; /** @brief Returns true if user wants audio export. */ bool selectedAudioExport() const; + /** @brief Show / hide proxy settings. */ + void updateProxyConfig(); + /** @brief Should we render using proxy clips. */ + bool proxyRendering(); public slots: void slotExport(bool scriptExport, int zoneIn, int zoneOut, const QString &playlistPath, const QString &scriptPath, bool exportAudio); diff --git a/src/widgets/configproject_ui.ui b/src/widgets/configproject_ui.ui index 33da0fea..398dac0d 100644 --- a/src/widgets/configproject_ui.ui +++ b/src/widgets/configproject_ui.ui @@ -6,14 +6,14 @@ 0 0 - 262 - 155 + 268 + 333 Form - + 0 @@ -139,7 +139,50 @@ - + + + + Proxy clips + + + false + + + + + + Enable proxy clips for HD + + + + + + + Generate automatically + + + + + + + Transcoding parameters + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical diff --git a/src/widgets/renderwidget_ui.ui b/src/widgets/renderwidget_ui.ui index 7dfc6004..be71963d 100644 --- a/src/widgets/renderwidget_ui.ui +++ b/src/widgets/renderwidget_ui.ui @@ -6,8 +6,8 @@ 0 0 - 362 - 554 + 391 + 575 @@ -390,21 +390,21 @@ - + Render to File - + Generate Script - + Qt::Horizontal @@ -417,14 +417,14 @@ - + Close - + @@ -453,6 +453,13 @@ + + + + Render using proxy clips + + +