#include <KDebug>
#include <QCryptographicHash>
+#include <QtConcurrentRun>
#include <cstdio>
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);
+ }
+}
+
#include <QPixmap>
#include <QObject>
#include <QTimer>
+#include <QProcess>
+#include <QFuture>
#include <KUrl>
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
bool m_placeHolder;
QList <CutZoneInfo> m_cutZones;
+
+ QFuture<void> m_proxyThread;
void setAudioThumbCreated(bool isDone);
/** Holds clip infos like fps, size,... */
QMap <QString, QString> properties() const;
QMap <QString, QString> metadata() const;
+private slots:
+ void slotGenerateProxy(QStringList parameters);
signals:
void gotAudioData();
+ void proxyReady(const QString, bool success);
};
#endif
</entry>
<entry name="profiles_list" type="UInt">
- <label>active project format.</label>
+ <label>Active project format.</label>
<default></default>
</entry>
+
+ <entry name="enableproxy" type="Bool">
+ <label>Enable proxy clips.</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="autoproxy" type="Bool">
+ <label>Automatically create proxy clips.</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="proxyparams" type="String">
+ <label>Proxy clips transcoding parameters.</label>
+ <default>-f avi -acodec libmp3lame -ac 2 -ab 92k -ar 48000 -vcodec mpeg2video -g 5 -deinterlace -s 480x270 -b 150k</default>
+ </entry>
+
</group>
<group name="timeline">
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.
void customChanged();
void doResetProfile();
void updateCaptureFolder();
+ void updateProxySettings();
};
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()));
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;
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 <QString, QString> proxies = m_projectList->getProxies();
+ QMapIterator<QString, QString> 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);
}
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
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);
#include <KIcon>
const int DurationRole = Qt::UserRole + 1;
+const int ProxyRole = Qt::UserRole + 5;
const int itemHeight = 38;
ProjectItem::ProjectItem(QTreeWidget * parent, DocClipBase *clip) :
}
}
+void ProjectItem::setProxyStatus(int status)
+{
+ setData(0, ProxyRole, status);
+}
+
#include <QTreeWidgetItem>
#include <QTreeWidget>
#include <QDomElement>
+#include <QProcess>
#include <KUrl>
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();
CLIPTYPE m_clipType;
QString m_clipId;
DocClipBase *m_clip;
+
};
#endif
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()) {
}
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)) {
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();
void ProjectList::reloadClipThumbnails()
{
- kDebug() << "////////////// RELOAD CLIPS THUMBNAILS!!!";
m_thumbnailQueue.clear();
QTreeWidgetItemIterator it(m_listView);
while (*it) {
} else emit displayMessage(i18n("Sequence not found"), -2);
}
+QMap <QString, QString> ProjectList::getProxies()
+{
+ QMap <QString, QString> list;
+ ProjectItem *item;
+ QTreeWidgetItemIterator it(m_listView);
+ while (*it) {
+ if ((*it)->type() != PROJECTCLIPTYPE) {
+ // subitem
+ ++it;
+ continue;
+ }
+ item = static_cast<ProjectItem *>(*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"
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()) {
/** @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 <QString, QString> getProxies();
public slots:
void setDocument(KdenliveDoc *doc);
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());
};
#endif
+
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);
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)));
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();
+}
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);
<rect>
<x>0</x>
<y>0</y>
- <width>262</width>
- <height>155</height>
+ <width>268</width>
+ <height>333</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
- <layout class="QGridLayout" name="gridLayout">
+ <layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
</property>
</spacer>
</item>
- <item row="2" column="0">
+ <item row="2" column="0" colspan="5">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Proxy clips</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="kcfg_enableproxy">
+ <property name="text">
+ <string>Enable proxy clips for HD</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="kcfg_autoproxy">
+ <property name="text">
+ <string>Generate automatically</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Transcoding parameters</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QPlainTextEdit" name="kcfg_proxyparams">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<rect>
<x>0</x>
<y>0</y>
- <width>362</width>
- <height>554</height>
+ <width>391</width>
+ <height>575</height>
</rect>
</property>
<property name="windowTitle">
</layout>
</widget>
</item>
- <item row="13" column="0" colspan="3">
+ <item row="14" column="0" colspan="3">
<widget class="QPushButton" name="buttonRender">
<property name="text">
<string>Render to File</string>
</property>
</widget>
</item>
- <item row="13" column="3" colspan="4">
+ <item row="14" column="3" colspan="4">
<widget class="QPushButton" name="buttonGenerateScript">
<property name="text">
<string>Generate Script</string>
</property>
</widget>
</item>
- <item row="13" column="7" colspan="2">
+ <item row="14" column="7" colspan="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
- <item row="13" column="9">
+ <item row="14" column="9">
<widget class="KPushButton" name="buttonClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
- <item row="14" column="0" colspan="10">
+ <item row="15" column="0" colspan="10">
<widget class="QGroupBox" name="errorBox">
<property name="title">
<string/>
</layout>
</widget>
</item>
+ <item row="13" column="0" colspan="10">
+ <widget class="QCheckBox" name="proxy_render">
+ <property name="text">
+ <string>Render using proxy clips</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">