diff -Nru kdenlive-22.04.1/CMakeLists.txt kdenlive-22.04.2/CMakeLists.txt
--- kdenlive-22.04.1/CMakeLists.txt 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/CMakeLists.txt 2022-06-12 20:11:07.000000000 +0000
@@ -6,7 +6,7 @@
# KDE Application Version, managed by release script
set (RELEASE_SERVICE_VERSION_MAJOR "22")
set (RELEASE_SERVICE_VERSION_MINOR "04")
-set (RELEASE_SERVICE_VERSION_MICRO "1")
+set (RELEASE_SERVICE_VERSION_MICRO "2")
set(KDENLIVE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
diff -Nru kdenlive-22.04.1/data/kdenlive.notifyrc kdenlive-22.04.2/data/kdenlive.notifyrc
--- kdenlive-22.04.1/data/kdenlive.notifyrc 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/data/kdenlive.notifyrc 2022-06-12 20:11:07.000000000 +0000
@@ -53,7 +53,7 @@
Name[az]=Görüntünün işlənməsi tamamlandı
Name[bs]=Iscrtavanje završeno
Name[ca]=Ha acabat la renderització
-Name[ca@valencia]=Ha acabat la renderització
+Name[ca@valencia]=Ha acabat la renderisació
Name[cs]=Renderování bylo dokončeno
Name[da]=Rendering gennemført
Name[de]=Rendern fertiggestellt
@@ -95,7 +95,7 @@
Comment[az]=Yığım tamamlandı
Comment[bs]=Iscrtavanje je gotovo
Comment[ca]=La renderització ja ha acabat
-Comment[ca@valencia]=La renderització ja ha acabat
+Comment[ca@valencia]=La renderisació ya ha acabat
Comment[cs]=Renderování je u konce
Comment[da]=Renderingen er slut
Comment[de]=Das Rendern ist beendet
@@ -140,7 +140,7 @@
Name[az]=Görüntü işlənməsi başladıldı
Name[bs]=Iscrtavanje započelo
Name[ca]=Ha començat la renderització
-Name[ca@valencia]=Ha començat la renderització
+Name[ca@valencia]=Ha començat la renderisació
Name[cs]=Renderování začalo
Name[da]=Rendering startet
Name[de]=Rendern wurde gestartet
@@ -182,7 +182,7 @@
Comment[az]=Görüntü işlənməsi başladılıb
Comment[bs]=Iscrtavanje je započelo
Comment[ca]=La renderització ja ha començat
-Comment[ca@valencia]=La renderització ja ha començat
+Comment[ca@valencia]=La renderisació ya ha començat
Comment[cs]=Vykreslování bylo zahájeno
Comment[da]=Renderingen blev startet
Comment[de]=Das Rendern wurde gestartet
diff -Nru kdenlive-22.04.1/data/knewstuff/kdenlive_renderprofiles.knsrc kdenlive-22.04.2/data/knewstuff/kdenlive_renderprofiles.knsrc
--- kdenlive-22.04.1/data/knewstuff/kdenlive_renderprofiles.knsrc 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/data/knewstuff/kdenlive_renderprofiles.knsrc 2022-06-12 20:11:07.000000000 +0000
@@ -3,7 +3,7 @@
Name[ar]=تشكيلة كدينلايف للتّصيير
Name[az]=Kdenlive görüntün işlənməsi profili
Name[ca]=Perfils de renderització del Kdenlive
-Name[ca@valencia]=Perfils de renderització de Kdenlive
+Name[ca@valencia]=Perfils de renderisació de Kdenlive
Name[cs]=Profily renderování Kdenlive
Name[da]=Kdenlive-renderingsprofiler
Name[de]=Kdenlive-Render-Profile
diff -Nru kdenlive-22.04.1/data/org.kde.kdenlive.appdata.xml kdenlive-22.04.2/data/org.kde.kdenlive.appdata.xml
--- kdenlive-22.04.1/data/org.kde.kdenlive.appdata.xml 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/data/org.kde.kdenlive.appdata.xml 2022-06-12 20:11:07.000000000 +0000
@@ -242,10 +242,10 @@
+
-
https://kdenlive.org/
https://bugs.kde.org
@@ -257,7 +257,7 @@
الخط الزمني في كدينلايڤ
Kdenlive zaman qrafiki
Línia de temps del Kdenlive
- Línia de temps de Kdenlive
+ Llínea de temps de Kdenlive
Časová osa Kdenlive
Kdenlive tidslinje
Kdenlive-Zeitleiste
diff -Nru kdenlive-22.04.1/data/profiles.xml kdenlive-22.04.2/data/profiles.xml
--- kdenlive-22.04.1/data/profiles.xml 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/data/profiles.xml 2022-06-12 20:11:07.000000000 +0000
@@ -8,7 +8,7 @@
qualities="15,45" defaultquality="25"
audiobitrates="256,64" defaultaudiobitrate="160"
args="f=mp4 movflags=+faststart vcodec=libx264 crf=%quality g=15 acodec=aac ab=%audiobitrate+'k'"
- speeds="preset=slower;preset=medium;preset=faster;preset=ultrafast"/>
+ speeds="preset=veryslow;preset=slower;preset=slow;preset=medium;preset=faster;preset=fast;preset=faster;preset=veryfast;preset=superfast;preset=ultrafast"/>
+ audioqualities="10,0" defaultaudioquality="5"
+ args="f=webm vcodec=libvpx-vp9 crf=%quality vb=15M qcomp=1 g=15 row-mt=1 tile-columns=4 frame-parallel=1 acodec=libopus compression_level=%audioquality"/>
+ speeds="preset=veryslow;preset=slower;preset=slow;preset=medium;preset=faster;preset=fast;preset=faster;preset=veryfast;preset=superfast;preset=ultrafast"/>
+ args="f=mp4 vcodec=h264_nvenc vb=%bitrate+'k' acodec=aac ab=%audiobitrate+'k'"/>
+ args="f=mp4 vcodec=h264_nvenc rc=constqp vglobal_quality=%quality vq=%quality acodec=aac ab=%audiobitrate+'k'"/>
4 and (float(sys.argv[4])>0 or float(sys.argv[5])>0):
- process = subprocess.Popen(['ffmpeg', '-loglevel', 'quiet', '-i',
+ process = subprocess.Popen([path, '-loglevel', 'quiet', '-i',
sys.argv[3], '-ss', sys.argv[4], '-t', sys.argv[5],
'-ar', str(sample_rate) , '-ac', '1', '-f', 's16le', '-'],
stdout=subprocess.PIPE)
else:
- process = subprocess.Popen(['ffmpeg', '-loglevel', 'quiet', '-i',
+ process = subprocess.Popen([path, '-loglevel', 'quiet', '-i',
sys.argv[3],
'-ar', str(sample_rate) , '-ac', '1', '-f', 's16le', '-'],
stdout=subprocess.PIPE)
diff -Nru kdenlive-22.04.1/debian/changelog kdenlive-22.04.2/debian/changelog
--- kdenlive-22.04.1/debian/changelog 2022-05-15 21:39:57.000000000 +0000
+++ kdenlive-22.04.2/debian/changelog 2022-06-12 20:11:09.000000000 +0000
@@ -1,8 +1,8 @@
-kdenlive (4:22.04.1-1~ubuntu22.04.1) jammy; urgency=low
+kdenlive (4:22.04.2-1~ubuntu22.04.1) jammy; urgency=low
* Auto build.
- -- Vincent Pinon Sun, 15 May 2022 21:39:57 +0000
+ -- Vincent Pinon Sun, 12 Jun 2022 20:11:09 +0000
kdenlive (4:21.12.1-0) focal; urgency=medium
diff -Nru kdenlive-22.04.1/debian/git-build-recipe.manifest kdenlive-22.04.2/debian/git-build-recipe.manifest
--- kdenlive-22.04.1/debian/git-build-recipe.manifest 2022-05-15 21:39:57.000000000 +0000
+++ kdenlive-22.04.2/debian/git-build-recipe.manifest 2022-06-12 20:11:09.000000000 +0000
@@ -1,4 +1,4 @@
-# git-build-recipe format 0.4 deb-version 4:22.04.1-1
-lp:kdenlive git-commit:0f5912285f51eab90a9e0123a38eebeb91dac7a2
+# git-build-recipe format 0.4 deb-version 4:22.04.2-1
+lp:kdenlive git-commit:0f311c6a64aed443977e044e4db1129a1460a7d9
merge packaging lp:kdenlive git-commit:7a45c5edb4ec76bf0cf9c46f6387436334eb5299
merge translations lp:kdenlive git-commit:79ba3ea15f18a547d69cc243cb578f735b12c1f6
diff -Nru kdenlive-22.04.1/.pc/.quilt_patches kdenlive-22.04.2/.pc/.quilt_patches
--- kdenlive-22.04.1/.pc/.quilt_patches 2022-05-15 21:39:57.000000000 +0000
+++ kdenlive-22.04.2/.pc/.quilt_patches 2022-06-12 20:11:09.000000000 +0000
@@ -1 +1 @@
-/home/buildd/build-RECIPEBRANCHBUILD-3363228/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches
+/home/buildd/build-RECIPEBRANCHBUILD-3377868/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches
diff -Nru kdenlive-22.04.1/.pc/.quilt_series kdenlive-22.04.2/.pc/.quilt_series
--- kdenlive-22.04.1/.pc/.quilt_series 2022-05-15 21:39:57.000000000 +0000
+++ kdenlive-22.04.2/.pc/.quilt_series 2022-06-12 20:11:09.000000000 +0000
@@ -1 +1 @@
-/home/buildd/build-RECIPEBRANCHBUILD-3363228/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches/series
+/home/buildd/build-RECIPEBRANCHBUILD-3377868/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches/series
diff -Nru kdenlive-22.04.1/src/assets/keyframes/view/keyframeview.cpp kdenlive-22.04.2/src/assets/keyframes/view/keyframeview.cpp
--- kdenlive-22.04.1/src/assets/keyframes/view/keyframeview.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/assets/keyframes/view/keyframeview.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -53,6 +53,15 @@
setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
connect(m_model.get(), &KeyframeModelList::modelChanged, this, &KeyframeView::slotModelChanged);
connect(m_model.get(), &KeyframeModelList::modelDisplayChanged, this, &KeyframeView::slotModelDisplayChanged);
+ m_centerConnection = connect(this, &KeyframeView::updateKeyframeOriginal, this, [&](int pos) {
+ m_currentKeyframeOriginal = pos;
+ update();
+ });
+}
+
+KeyframeView::~KeyframeView()
+{
+ QObject::disconnect(m_centerConnection);
}
void KeyframeView::slotModelChanged()
@@ -268,13 +277,11 @@
break;
}
Fun local_redo = [this, position = m_position]() {
- m_currentKeyframeOriginal = position;
- update();
+ emit updateKeyframeOriginal(position);
return true;
};
Fun local_undo = [this, sourcePosition]() {
- m_currentKeyframeOriginal = sourcePosition;
- update();
+ emit updateKeyframeOriginal(sourcePosition);
return true;
};
local_redo();
diff -Nru kdenlive-22.04.1/src/assets/keyframes/view/keyframeview.hpp kdenlive-22.04.2/src/assets/keyframes/view/keyframeview.hpp
--- kdenlive-22.04.1/src/assets/keyframes/view/keyframeview.hpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/assets/keyframes/view/keyframeview.hpp 2022-06-12 20:11:07.000000000 +0000
@@ -20,6 +20,7 @@
public:
explicit KeyframeView(std::shared_ptr model, int duration, QWidget *parent = nullptr);
+ ~KeyframeView() override;
void setDuration(int dur, int inPoint);
const QString getAssetId();
/** @brief Copy a keyframe parameter to selected keyframes. */
@@ -94,12 +95,14 @@
QColor m_colSelected;
QColor m_colKeyframe;
QColor m_colKeyframeBg;
+ QMetaObject::Connection m_centerConnection;
signals:
void seekToPos(int pos);
void atKeyframe(bool isKeyframe, bool singleKeyframe);
void modified();
void activateEffect();
+ void updateKeyframeOriginal(int pos);
};
#endif
diff -Nru kdenlive-22.04.1/src/audiomixer/mixermanager.cpp kdenlive-22.04.2/src/audiomixer/mixermanager.cpp
--- kdenlive-22.04.1/src/audiomixer/mixermanager.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/audiomixer/mixermanager.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -108,7 +108,9 @@
m_channelsLayout->insertWidget(0, line);
m_channelsLayout->insertWidget(0, mixer.get());
m_recommendedWidth = (mixer->minimumWidth() + 12 + line->minimumWidth()) * (qMin(2, int(m_mixers.size())));
- m_channelsBox->setMinimumWidth(m_recommendedWidth);
+ if (!KdenliveSettings::mixerCollapse()) {
+ m_channelsBox->setMinimumWidth(m_recommendedWidth);
+ }
}
void MixerManager::deregisterTrack(int tid)
diff -Nru kdenlive-22.04.1/src/bin/bin.cpp kdenlive-22.04.2/src/bin/bin.cpp
--- kdenlive-22.04.1/src/bin/bin.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/bin/bin.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -683,7 +683,6 @@
void MyListView::mousePressEvent(QMouseEvent *event)
{
- QListView::mousePressEvent(event);
if (event->button() == Qt::LeftButton) {
QModelIndex ix = indexAt(event->pos());
if (ix.isValid()) {
@@ -696,7 +695,7 @@
}
emit updateDragMode(m_dragType);
}
- event->accept();
+ QListView::mousePressEvent(event);
}
void MyListView::mouseMoveEvent(QMouseEvent *event)
@@ -706,6 +705,7 @@
QModelIndexList indexes = selectedIndexes();
if (indexes.isEmpty()) {
// Dragging from empty zone, abort
+ QListView::mouseMoveEvent(event);
return;
}
auto *drag = new QDrag(this);
@@ -734,7 +734,9 @@
}
drag->exec();
emit processDragEnd();
+ return;
}
+ QListView::mouseMoveEvent(event);
return;
}
QModelIndex index = indexAt(event->pos());
@@ -829,8 +831,10 @@
int distance = (event->pos() - m_startPos).manhattanLength();
if (distance >= QApplication::startDragDistance()) {
dragged = performDrag();
+ return;
}
}
+ QTreeView::mouseMoveEvent(event);
return;
} else {
QModelIndex index = indexAt(event->pos());
@@ -932,6 +936,7 @@
drag->setPixmap(QPixmap::fromImage(image));
}
drag->exec();
+ drag->deleteLater();
emit processDragEnd();
return true;
}
@@ -2260,7 +2265,6 @@
m_itemView->scrollTo(m_proxyModel->mapFromSource(ix), QAbstractItemView::EnsureVisible);
}
} else {
- m_proxyModel->selectionModel()->clearSelection();
std::shared_ptr clip = getBinClip(clipId);
if (clip == nullptr) {
return;
@@ -2295,8 +2299,10 @@
// Set item as current so that it displays its content in clip monitor
setCurrent(currentItem);
AbstractProjectItem::PROJECTITEMTYPE itemType = currentItem->itemType();
+ bool isClip = itemType == AbstractProjectItem::ClipItem;
+ bool isFolder = itemType == AbstractProjectItem::FolderItem;
std::shared_ptr clip = nullptr;
- if (itemType == AbstractProjectItem::ClipItem) {
+ if (isClip) {
clip = std::static_pointer_cast(currentItem);
m_tagsWidget->setTagData(clip->tags());
m_deleteAction->setText(i18n("Delete Clip"));
@@ -2319,7 +2325,7 @@
if (clip && clip->statusReady()) {
emit requestShowClipProperties(clip, false);
m_proxyAction->blockSignals(true);
- if (itemType == AbstractProjectItem::ClipItem) {
+ if (isClip) {
emit findInTimeline(clip->clipId(), clip->timelineInstances());
}
clipService = clip->getProducerProperty(QStringLiteral("mlt_service"));
@@ -2335,30 +2341,30 @@
emit findInTimeline(QString());
m_openAction->setEnabled(false);
}
- m_clipsActionsMenu->setEnabled(itemType != AbstractProjectItem::FolderItem);
- m_editAction->setVisible(itemType != AbstractProjectItem::FolderItem);
+ m_clipsActionsMenu->setEnabled(!isFolder);
+ m_editAction->setVisible(!isFolder);
m_editAction->setEnabled(true);
m_extractAudioAction->menuAction()->setVisible(hasAudio);
m_extractAudioAction->setEnabled(hasAudio);
m_openAction->setEnabled(type == ClipType::Image || type == ClipType::Audio || type == ClipType::TextTemplate || type == ClipType::Text);
- m_openAction->setVisible(itemType != AbstractProjectItem::FolderItem);
- m_duplicateAction->setEnabled(itemType == AbstractProjectItem::ClipItem);
- m_duplicateAction->setVisible(itemType != AbstractProjectItem::FolderItem);
- m_inTimelineAction->setEnabled(itemType == AbstractProjectItem::ClipItem);
- m_inTimelineAction->setVisible(itemType == AbstractProjectItem::ClipItem);
- m_locateAction->setEnabled(itemType != AbstractProjectItem::FolderItem && isImported);
- m_locateAction->setVisible(itemType != AbstractProjectItem::FolderItem && isImported);
- m_proxyAction->setEnabled(m_doc->useProxy() && itemType != AbstractProjectItem::FolderItem);
- m_reloadAction->setEnabled(itemType == AbstractProjectItem::ClipItem);
- m_reloadAction->setVisible(itemType != AbstractProjectItem::FolderItem);
- m_replaceAction->setEnabled(itemType == AbstractProjectItem::ClipItem);
- m_replaceAction->setVisible(itemType != AbstractProjectItem::FolderItem);
+ m_openAction->setVisible(!isFolder);
+ m_duplicateAction->setEnabled(isClip);
+ m_duplicateAction->setVisible(!isFolder);
+ m_inTimelineAction->setEnabled(isClip);
+ m_inTimelineAction->setVisible(isClip);
+ m_locateAction->setEnabled(!isFolder && isImported);
+ m_locateAction->setVisible(!isFolder && isImported);
+ m_proxyAction->setEnabled(m_doc->useProxy() && !isFolder);
+ m_reloadAction->setEnabled(isClip);
+ m_reloadAction->setVisible(!isFolder);
+ m_replaceAction->setEnabled(isClip);
+ m_replaceAction->setVisible(!isFolder);
m_clipsActionsMenu->menuAction()->setVisible(
- itemType != AbstractProjectItem::FolderItem &&
+ !isFolder &&
(clipService.contains(QStringLiteral("avformat")) || clipService.contains(QStringLiteral("xml")) || clipService.contains(QStringLiteral("consumer"))));
- m_transcodeAction->setEnabled(itemType != AbstractProjectItem::FolderItem);
- m_transcodeAction->setVisible(itemType != AbstractProjectItem::FolderItem && (type == ClipType::Playlist || type == ClipType::Text || clipService.contains(QStringLiteral("avformat"))));
+ m_transcodeAction->setEnabled(!isFolder);
+ m_transcodeAction->setVisible(!isFolder && (type == ClipType::Playlist || type == ClipType::Text || clipService.contains(QStringLiteral("avformat"))));
m_deleteAction->setEnabled(true);
m_renameAction->setEnabled(true);
@@ -3027,14 +3033,14 @@
view->expand(m_proxyModel->mapFromSource(ix.parent()));
const QModelIndex id2 = m_itemModel->index(row, m_itemModel->columnCount() - 1, ix.parent());
if (id.isValid() && id2.isValid()) {
- m_proxyModel->selectionModel()->select(QItemSelection(m_proxyModel->mapFromSource(id), m_proxyModel->mapFromSource(id2)), QItemSelectionModel::SelectCurrent);
+ m_proxyModel->selectionModel()->select(QItemSelection(m_proxyModel->mapFromSource(id), m_proxyModel->mapFromSource(id2)), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
}
} else {
// Ensure parent folder is currently opened
m_itemView->setRootIndex(m_proxyModel->mapFromSource(ix.parent()));
m_upAction->setEnabled(!ix.parent().data(AbstractProjectItem::DataId).toString().isEmpty());
if (id.isValid()) {
- m_proxyModel->selectionModel()->setCurrentIndex(m_proxyModel->mapFromSource(id), QItemSelectionModel::ClearAndSelect);
+ m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(id), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
}
}
m_itemView->scrollTo(m_proxyModel->mapFromSource(ix), QAbstractItemView::EnsureVisible);
@@ -4920,9 +4926,8 @@
{
if (m_transcodingDialog == nullptr) {
m_transcodingDialog = new TranscodeSeek(this);
- connect(m_transcodingDialog, &QDialog::accepted, this, [&] () {
- QMap ids = m_transcodingDialog->ids();
- QString firstId = ids.firstKey();
+ connect(m_transcodingDialog, &QDialog::accepted, this, [&]() {
+ QMap ids = m_transcodingDialog->ids();
QMapIterator i(ids);
while (i.hasNext()) {
i.next();
@@ -4932,8 +4937,7 @@
m_transcodingDialog->deleteLater();
m_transcodingDialog = nullptr;
});
- connect(m_transcodingDialog, &QDialog::rejected, this, [&] () {
- QString firstId = m_transcodingDialog->ids().firstKey();
+ connect(m_transcodingDialog, &QDialog::rejected, this, [&]() {
m_transcodingDialog->deleteLater();
m_transcodingDialog = nullptr;
});
@@ -4961,21 +4965,27 @@
m_transcodingDialog = new TranscodeSeek(this);
connect(m_transcodingDialog, &QDialog::accepted, this, [&, checkProfile] () {
QMap ids = m_transcodingDialog->ids();
- QString firstId = ids.firstKey();
- QMapIterator i(ids);
- while (i.hasNext()) {
- i.next();
- std::shared_ptr clip = m_itemModel->getClipByBinID(i.key());
- TranscodeTask::start({ObjectType::BinClip,i.key().toInt()}, i.value().first(), m_transcodingDialog->preParams(), m_transcodingDialog->params(i.value().at(1).toInt()), -1, -1, true, clip.get(), false, i.key() == firstId ? checkProfile : false);
+ if (!ids.isEmpty()) {
+ QString firstId = ids.firstKey();
+ QMapIterator i(ids);
+ while (i.hasNext()) {
+ i.next();
+ std::shared_ptr clip = m_itemModel->getClipByBinID(i.key());
+ TranscodeTask::start({ObjectType::BinClip,i.key().toInt()}, i.value().first(), m_transcodingDialog->preParams(), m_transcodingDialog->params(i.value().at(1).toInt()), -1, -1, true, clip.get(), false, i.key() == firstId ? checkProfile : false);
+ }
}
m_transcodingDialog->deleteLater();
m_transcodingDialog = nullptr;
});
connect(m_transcodingDialog, &QDialog::rejected, this, [&, checkProfile] () {
- QString firstId = m_transcodingDialog->ids().firstKey();
+ QMap ids = m_transcodingDialog->ids();
+ QString firstId;
+ if (!ids.isEmpty()) {
+ firstId = ids.firstKey();
+ }
m_transcodingDialog->deleteLater();
m_transcodingDialog = nullptr;
- if (checkProfile) {
+ if (checkProfile && !firstId.isEmpty()) {
pCore->bin()->slotCheckProfile(firstId);
}
});
diff -Nru kdenlive-22.04.1/src/bin/filewatcher.cpp kdenlive-22.04.2/src/bin/filewatcher.cpp
--- kdenlive-22.04.1/src/bin/filewatcher.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/bin/filewatcher.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -71,7 +71,7 @@
m_occurences[url].erase(binId);
m_binClipPaths.erase(binId);
if (m_occurences[url].empty()) {
- KDirWatch::self()->removeFile(url);
+ m_fileWatcher->removeFile(url);
m_occurences.erase(url);
}
}
@@ -106,7 +106,7 @@
{
auto checkList = m_modifiedUrls;
for (const QString &path : checkList) {
- if (KDirWatch::self()->ctime(path).msecsTo(QDateTime::currentDateTime()) > 2000) {
+ if (m_fileWatcher->ctime(path).msecsTo(QDateTime::currentDateTime()) > 2000) {
for (const QString &id : m_occurences[path]) {
emit binClipModified(id);
}
@@ -120,17 +120,17 @@
void FileWatcher::clear()
{
- KDirWatch::self()->stopScan();
+ m_fileWatcher->stopScan();
for (const auto &f : m_occurences) {
- KDirWatch::self()->removeFile(f.first);
+ m_fileWatcher->removeFile(f.first);
}
m_occurences.clear();
m_modifiedUrls.clear();
m_binClipPaths.clear();
- KDirWatch::self()->startScan();
+ m_fileWatcher->startScan();
}
bool FileWatcher::contains(const QString &path) const
{
- return KDirWatch::self()->contains(path);
+ return m_fileWatcher->contains(path);
}
diff -Nru kdenlive-22.04.1/src/bin/model/markerlistmodel.cpp kdenlive-22.04.2/src/bin/model/markerlistmodel.cpp
--- kdenlive-22.04.1/src/bin/model/markerlistmodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/bin/model/markerlistmodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -54,27 +54,40 @@
connect(this, &MarkerListModel::dataChanged, this, &MarkerListModel::modelChanged);
}
-bool MarkerListModel::hasMarker(GenTime pos) const
+int MarkerListModel::markerIdAtFrame(int pos) const
{
- std::map::const_iterator it = m_markerList.begin();
- while (it != m_markerList.end()) {
- if (it->second.time() == pos) {
- return true;
- }
- it++;
+ if (m_markerPositions.contains(pos)) {
+ return m_markerPositions.value(pos);
}
- return false;
+ return -1;
}
+bool MarkerListModel::hasMarker(GenTime pos) const
+{
+ int frame = pos.frames(pCore->getCurrentFps());
+ return hasMarker(frame);
+}
+
+CommentedTime MarkerListModel::markerById(int mid) const
+{
+ Q_ASSERT(m_markerPositions.values().contains(mid));
+ return m_markerList.at(mid);
+}
+
+CommentedTime MarkerListModel::marker(int frame) const
+{
+ int mid = markerIdAtFrame(frame);
+ if (mid > -1) {
+ return m_markerList.at(mid);
+ }
+ return CommentedTime();
+}
CommentedTime MarkerListModel::marker(GenTime pos) const
{
- std::map::const_iterator it = m_markerList.begin();
- while (it != m_markerList.end()) {
- if (it->second.time() == pos) {
- return it->second;
- }
- it++;
+ int mid = markerIdAtFrame(pos.frames(pCore->getCurrentFps()));
+ if (mid > -1) {
+ return m_markerList.at(mid);
}
return CommentedTime();
}
@@ -213,13 +226,14 @@
int MarkerListModel::getIdFromPos(const GenTime &pos) const
{
- READ_LOCK();
- std::map::const_iterator it = m_markerList.begin();
- while (it != m_markerList.end()) {
- if (it->second.time() == pos) {
- return it->first;
- }
- it++;
+ int frame = pos.frames(pCore->getCurrentFps());
+ return getIdFromPos(frame);
+}
+
+int MarkerListModel::getIdFromPos(int frame) const
+{
+ if (m_markerPositions.contains(frame)) {
+ return m_markerPositions.value(frame);
}
return -1;
}
@@ -233,7 +247,10 @@
return false;
}
int row = getRowfromId(mid);
+ int oldPos = m_markerList.at(mid).time().frames(pCore->getCurrentFps());
m_markerList[mid].setTime(pos);
+ m_markerPositions.remove(oldPos);
+ m_markerPositions.insert(pos.frames(pCore->getCurrentFps()), mid);
emit dataChanged(index(row), index(row), {FrameRole});
return true;
}
@@ -248,7 +265,10 @@
int lastRow = -1;
for (auto mid : markersId) {
Q_ASSERT(m_markerList.count(mid) > 0);
- GenTime t = m_markerList.at(mid).time() + GenTime(offset, pCore->getCurrentFps());
+ GenTime t = m_markerList.at(mid).time();
+ m_markerPositions.remove(t.frames(pCore->getCurrentFps()));
+ t += GenTime(offset, pCore->getCurrentFps());
+ m_markerPositions.insert(t.frames(pCore->getCurrentFps()), mid);
m_markerList[mid].setTime(t);
if (!updateView) {
continue;
@@ -326,6 +346,7 @@
int insertionRow = static_cast(model->m_markerList.size());
model->beginInsertRows(QModelIndex(), insertionRow, insertionRow);
model->m_markerList[mid] = CommentedTime(pos, comment, type);
+ model->m_markerPositions.insert(pos.frames(pCore->getCurrentFps()), mid);
model->endInsertRows();
model->addSnapPoint(pos);
return true;
@@ -344,6 +365,7 @@
int row = model->getRowfromId(mid);
model->beginRemoveRows(QModelIndex(), row, row);
model->m_markerList.erase(mid);
+ model->m_markerPositions.remove(pos.frames(pCore->getCurrentFps()));
model->endRemoveRows();
model->removeSnapPoint(pos);
return true;
@@ -434,6 +456,18 @@
return static_cast(m_markerList.size());
}
+CommentedTime MarkerListModel::getMarker(int frame, bool *ok) const
+{
+ READ_LOCK();
+ if (hasMarker(frame) == false) {
+ // return empty marker
+ *ok = false;
+ return CommentedTime();
+ }
+ *ok = true;
+ return marker(frame);
+}
+
CommentedTime MarkerListModel::getMarker(const GenTime &pos, bool *ok) const
{
READ_LOCK();
@@ -461,13 +495,12 @@
QList MarkerListModel::getMarkersInRange(int start, int end) const
{
- READ_LOCK();
QList markers;
- for (const auto &marker : m_markerList) {
- int pos = marker.second.time().frames(pCore->getCurrentFps());
- if(pos >= start && (end == -1 || pos <= end)) {
- markers << marker.second;
- }
+ QVector mids = getMarkersIdInRange(start, end);
+ // Now extract markers
+ READ_LOCK();
+ for (const auto &marker : mids) {
+ markers << m_markerList.at(marker);
}
std::sort(markers.begin(), markers.end());
return markers;
@@ -477,25 +510,24 @@
{
READ_LOCK();
Q_ASSERT(m_markerList.count(mid) > 0);
- return m_markerList.at(mid).time().frames(pCore->getCurrentFps());
+ return m_markerPositions.key(mid);
}
QVector MarkerListModel::getMarkersIdInRange(int start, int end) const
{
READ_LOCK();
+ // First find marker ids in range
QVector markers;
- // Ensure we provide sorted markers list
- std::map sortedList;
-
- for(std::map::const_iterator it = m_markerList.begin(); it != m_markerList.end(); ++it)
- sortedList.insert(std::pair(it -> second, it -> first));
-
- for (const auto &marker : sortedList) {
- int pos = marker.first.time().frames(pCore->getCurrentFps());
- if(pos >= start && (end == -1 || pos <= end)) {
- markers << marker.second;
+ QMap::const_iterator i = m_markerPositions.constBegin();
+ while (i != m_markerPositions.constEnd()) {
+ if (end > -1 && i.key() > end) {
+ break;
+ }
+ if (i.key() >= start) {
+ markers << i.value();
}
+ ++i;
}
return markers;
}
@@ -503,17 +535,15 @@
std::vector MarkerListModel::getSnapPoints() const
{
READ_LOCK();
- std::vector markers;
- for (const auto &marker : m_markerList) {
- markers.push_back(marker.second.time().frames(pCore->getCurrentFps()));
- }
+ const QList positions = m_markerPositions.keys();
+ std::vector markers(positions.cbegin(), positions.cend());
return markers;
}
bool MarkerListModel::hasMarker(int frame) const
{
READ_LOCK();
- return hasMarker(GenTime(frame, pCore->getCurrentFps()));
+ return m_markerPositions.contains(frame);
}
void MarkerListModel::registerSnapModel(const std::weak_ptr &snapModel)
@@ -525,9 +555,10 @@
m_registeredSnaps.push_back(snapModel);
// we now add the already existing markers to the snap
- for (const auto &marker : m_markerList) {
- qDebug() << " *- *-* REGISTERING MARKER: " << marker.second.time().frames(pCore->getCurrentFps());
- ptr->addPoint(marker.second.time().frames(pCore->getCurrentFps()));
+ QMap::const_iterator i = m_markerPositions.constBegin();
+ while (i != m_markerPositions.constEnd()) {
+ ptr->addPoint(i.key());
+ ++i;
}
} else {
qDebug() << "Error: added snapmodel is null";
diff -Nru kdenlive-22.04.1/src/bin/model/markerlistmodel.hpp kdenlive-22.04.2/src/bin/model/markerlistmodel.hpp
--- kdenlive-22.04.1/src/bin/model/markerlistmodel.hpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/bin/model/markerlistmodel.hpp 2022-06-12 20:11:07.000000000 +0000
@@ -89,6 +89,7 @@
/** @brief Returns a marker data at given pos */
CommentedTime getMarker(const GenTime &pos, bool *ok) const;
+ CommentedTime getMarker(int frame, bool *ok) const;
/** @brief Returns all markers in model or – if a type is given – all markers of the given type */
QList getAllMarkers(int type = -1) const;
@@ -110,8 +111,13 @@
Notice that add/remove queries are done in real time (gentime), but this request is made in frame
*/
Q_INVOKABLE bool hasMarker(int frame) const;
+ /** @brief Returns a marker id at frame pos. Returns -1 if no marker exists at that position
+ */
+ int markerIdAtFrame(int pos) const;
bool hasMarker(GenTime pos) const;
CommentedTime marker(GenTime pos) const;
+ CommentedTime marker(int frame) const;
+ CommentedTime markerById(int mid) const;
/** @brief Registers a snapModel to the marker model.
This is intended to be used for a guide model, so that the timelines can register their snapmodel to be updated when the guide moves. This is also used
@@ -185,9 +191,12 @@
mutable QReadWriteLock m_lock;
std::map m_markerList;
+ /** @brief A list of {marker frame,marker id}, useful to quickly find a marker */
+ QMap m_markerPositions;
std::vector> m_registeredSnaps;
int getRowfromId(int mid) const;
int getIdFromPos(const GenTime &pos) const;
+ int getIdFromPos(int frame) const;
signals:
void modelChanged();
diff -Nru kdenlive-22.04.1/src/bin/projectclip.cpp kdenlive-22.04.2/src/bin/projectclip.cpp
--- kdenlive-22.04.1/src/bin/projectclip.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/bin/projectclip.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -408,7 +408,6 @@
if (!xml.isNull()) {
bool hashChanged = false;
m_thumbsProducer.reset();
- m_clipStatus = FileStatus::StatusWaiting;
ClipType::ProducerType type = clipType();
if (type != ClipType::Color && type != ClipType::Image && type != ClipType::SlideShow) {
xml.removeAttribute("out");
@@ -430,6 +429,7 @@
discardAudioThumb();
}
ThumbnailCache::get()->invalidateThumbsForClip(clipId());
+ m_clipStatus = FileStatus::StatusWaiting;
m_thumbsProducer.reset();
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, xml, false, -1, -1, this);
}
@@ -991,6 +991,12 @@
if (secondPlaylist) {
tid = -tid;
}
+ if (m_audioProducers.find(tid) != m_audioProducers.end()) {
+ // Buggy project, all clips in a track should use the same track producer, fix
+ qDebug()<<"/// FOUND INCORRECT PRODUCER ON AUDIO TRACK; FIXING";
+ std::shared_ptr prod(getTimelineProducer(tid, clipId, state, master->parent().get_int("audio_index"), speed)->cut(in, out));
+ return {prod, false};
+ }
m_audioProducers[tid] = std::make_shared(&master->parent());
m_effectStack->loadService(m_audioProducers[tid]);
return {master, true};
@@ -1002,6 +1008,12 @@
if (secondPlaylist) {
tid = -tid;
}
+ if (m_videoProducers.find(tid) != m_videoProducers.end()) {
+ qDebug()<<"/// FOUND INCORRECT PRODUCER ON VIDEO TRACK; FIXING";
+ // Buggy project, all clips in a track should use the same track producer, fix
+ std::shared_ptr prod(getTimelineProducer(tid, clipId, state, master->parent().get_int("audio_index"), speed)->cut(in, out));
+ return {prod, false};
+ }
m_videoProducers[tid] = std::make_shared(&master->parent());
m_effectStack->loadService(m_videoProducers[tid]);
} else {
diff -Nru kdenlive-22.04.1/src/bin/projectsortproxymodel.cpp kdenlive-22.04.2/src/bin/projectsortproxymodel.cpp
--- kdenlive-22.04.1/src/bin/projectsortproxymodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/bin/projectsortproxymodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -158,15 +158,38 @@
void ProjectSortProxyModel::onCurrentRowChanged(const QItemSelection ¤t, const QItemSelection &previous)
{
Q_UNUSED(previous)
- QModelIndexList indexes = current.indexes();
+ // Warning: the "current" parameter only represents the item that was newly selected, but not all selected items
+ QModelIndexList indexes = m_selection->selectedIndexes();
if (indexes.isEmpty()) {
+ // No item selected
emit selectModel(QModelIndex());
return;
}
- for (int ix = 0; ix < indexes.count(); ix++) {
- if (indexes.at(ix).column() == 0 || indexes.at(ix).column() == 7) {
- emit selectModel(indexes.at(ix));
- break;
+ if (indexes.contains(m_selection->currentIndex())) {
+ // Select current item
+ emit selectModel(m_selection->currentIndex());
+ } else {
+ QModelIndexList newlySelected = current.indexes();
+ if (!newlySelected.isEmpty()) {
+ QModelIndex ix = newlySelected.takeLast();
+ while (ix.column() != 0 && !newlySelected.isEmpty()) {
+ ix = newlySelected.takeLast();
+ }
+ if (ix .column() == 0) {
+ emit selectModel(ix);
+ return;
+ }
+ } else {
+ if (!indexes.isEmpty()) {
+ QModelIndex ix = indexes.takeLast();
+ while (ix.column() != 0 && !indexes.isEmpty()) {
+ ix = indexes.takeLast();
+ }
+ if (ix .column() == 0) {
+ emit selectModel(ix);
+ return;
+ }
+ }
}
}
}
diff -Nru kdenlive-22.04.1/src/dialogs/renderpresetdialog.cpp kdenlive-22.04.2/src/dialogs/renderpresetdialog.cpp
--- kdenlive-22.04.1/src/dialogs/renderpresetdialog.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/dialogs/renderpresetdialog.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -64,6 +64,7 @@
QStringLiteral("qp_b"),
QStringLiteral("ab"),
QStringLiteral("aq"),
+ QStringLiteral("compression_level"),
QStringLiteral("vbr"),
QStringLiteral("ar"),
QStringLiteral("display_aspect_num"),
@@ -307,6 +308,9 @@
audioSampleRate->setCurrentText(preset->getParam(QStringLiteral("ar")));
QString aqParam = preset->getParam(QStringLiteral("aq"));
+ if (aqParam.isEmpty()) {
+ aqParam = preset->getParam(QStringLiteral("compression_level"));
+ }
if (aqParam.contains(QStringLiteral("%audioquality"))) {
aQuality->setValue(preset->defaultAQuality().toInt());
} else {
@@ -704,9 +708,7 @@
}
} else if (acodec == "libopus") {
params.append(QStringLiteral("vbr=on"));
- // TODO
- //params.append(QStringLiteral("compression_level= "));
- //setIfNotSet(p, "compression_level", TO_ABSOLUTE(0, 10, ui->audioQualitySpinner->value()));
+ params.append(QStringLiteral("compression_level=%audioquality"));
} else {
params.append(QStringLiteral("aq=%audioquality"));
}
diff -Nru kdenlive-22.04.1/src/dialogs/renderwidget.cpp kdenlive-22.04.2/src/dialogs/renderwidget.cpp
--- kdenlive-22.04.1/src/dialogs/renderwidget.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/dialogs/renderwidget.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -171,6 +171,7 @@
}
});
m_view.optionsGroup->setVisible(m_view.options->isChecked());
+ m_view.optionsGroup->setMinimumWidth(m_view.optionsGroup->width() + m_view.optionsGroup->verticalScrollBar()->width());
connect(m_view.options, &QAbstractButton::toggled, m_view.optionsGroup, &QWidget::setVisible);
connect(m_view.out_file, &KUrlRequester::textChanged, this, static_cast(&RenderWidget::slotUpdateButtons));
@@ -335,7 +336,7 @@
QSize RenderWidget::sizeHint() const
{
// Make sure the widget has minimum size on opening
- return {200, 200};
+ return {200, qMax(200, screen()->availableGeometry().height())};
}
RenderWidget::~RenderWidget()
@@ -1333,19 +1334,25 @@
m_view.qualityGroup->setEnabled(false);
}
+ // historically qualities are sorted from best to worse for some reason
+ int vmin = preset->videoQualities().last().toInt();
+ int vmax = preset->videoQualities().first().toInt();
+ int vrange = abs(vmax - vmin);
+ int amin = preset->audioQualities().last().toInt();
+ int amax = preset->audioQualities().first().toInt();
+ int arange = abs(amax - amin);
+
+ m_view.quality->setMaximum(qMin(100, qMax(vrange, arange)));
double percent = double(m_view.quality->value()) / double(m_view.quality->maximum());
m_view.qualityPercent->setText(QStringLiteral("%1%").arg(qRound(percent * 100)));
- // historically qualities are sorted from best to worse for some reason
- int min = preset->videoQualities().last().toInt();
- int max = preset->videoQualities().first().toInt();
+
int val = preset->defaultVQuality().toInt();
+
if (m_view.qualityGroup->isChecked()) {
- if (min < max) {
- int range = max - min;
- val = min + int(range * percent);
+ if (vmin < vmax) {
+ val = vmin + int(vrange * percent);
} else {
- int range = min - max;
- val = min - int(range * percent);
+ val = vmin - int(vrange * percent);
}
}
params.replace(QStringLiteral("%quality"), QString::number(val));
@@ -1360,17 +1367,12 @@
// cvbr = Constrained Variable Bit Rate
params.replace(QStringLiteral("%cvbr"), QString::number(val));
- // historically qualities are sorted from best to worse for some reason
- min = preset->audioQualities().last().toInt();
- max = preset->audioQualities().first().toInt();
val = preset->defaultAQuality().toInt();
if (m_view.qualityGroup->isChecked()) {
- if (min < max) {
- int range = max - min;
- val = min + int(range * percent);
+ if (amin < amax) {
+ val = amin + int(arange * percent);
} else {
- int range = min - max;
- val = min - int(range * percent);
+ val = amin - int(arange * percent);
}
}
params.replace(QStringLiteral("%audioquality"), QString::number(val));
diff -Nru kdenlive-22.04.1/src/doc/documentchecker.cpp kdenlive-22.04.2/src/doc/documentchecker.cpp
--- kdenlive-22.04.1/src/doc/documentchecker.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/doc/documentchecker.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -782,7 +782,7 @@
m_missingClips.append(e);
missingPaths.append(resource);
}
- } else if (service.startsWith(QLatin1String("avformat")) || slideshow) {
+ } else if (service.startsWith(QLatin1String("avformat")) || slideshow || service == QLatin1String("qimage") || service == QLatin1String("pixbuf")) {
// Check if file changed
const QByteArray hash = Xml::getXmlProperty(e, "kdenlive:file_hash").toLatin1();
if (!hash.isEmpty()) {
diff -Nru kdenlive-22.04.1/src/doc/kdenlivedoc.cpp kdenlive-22.04.2/src/doc/kdenlivedoc.cpp
--- kdenlive-22.04.1/src/doc/kdenlivedoc.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/doc/kdenlivedoc.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -1839,8 +1839,15 @@
// replace proxy clips with originals
QMap proxies = pCore->projectItemModel()->getProxies(root);
-
QDomNodeList producers = doc.elementsByTagName(QStringLiteral("producer"));
+ QDomNodeList chains = doc.elementsByTagName(QStringLiteral("chain"));
+ processProxyNodes(producers, root, proxies);
+ processProxyNodes(chains, root, proxies);
+}
+
+void KdenliveDoc::processProxyNodes(QDomNodeList producers, const QString &root, const QMap &proxies)
+{
+
QString producerResource;
QString producerService;
QString originalProducerService;
diff -Nru kdenlive-22.04.1/src/doc/kdenlivedoc.h kdenlive-22.04.2/src/doc/kdenlivedoc.h
--- kdenlive-22.04.1/src/doc/kdenlivedoc.h 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/doc/kdenlivedoc.h 2022-06-12 20:11:07.000000000 +0000
@@ -173,6 +173,7 @@
double getDocumentVersion() const;
/** @brief Replace proxy clips with originals for rendering. */
void useOriginals(QDomDocument &doc);
+ void processProxyNodes(QDomNodeList producers, const QString &root, const QMap &proxies);
private:
QUrl m_url;
diff -Nru kdenlive-22.04.1/src/effects/effectstack/model/effectstackmodel.cpp kdenlive-22.04.2/src/effects/effectstack/model/effectstackmodel.cpp
--- kdenlive-22.04.1/src/effects/effectstack/model/effectstackmodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/effects/effectstack/model/effectstackmodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -876,6 +876,24 @@
if (!m_loadingExisting) {
// qDebug() << "$$$$$$$$$$$$$$$$$$$$$ Planting effect in " << m_childServices.size();
effectItem->plant(m_masterService);
+ // Check if we have an internal effect that needs to stay on top
+ if (m_ownerId.first == ObjectType::Master || m_ownerId.first == ObjectType::TimelineTrack) {
+ // check for subtitle effect
+ auto ms = m_masterService.lock();
+ int ct = ms->filter_count();
+ QVector ixToMove;
+ for (int i = 0; i < ct; i++) {
+ if (ms->filter(i)->get_int("internal_added") > 0) {
+ ixToMove << i;
+ }
+ }
+ std::sort(ixToMove.rbegin(), ixToMove.rend());
+ for (auto &ix : ixToMove) {
+ if (ix < ct - 1) {
+ ms->move_filter(ix, ct - 1);
+ }
+ }
+ }
for (const auto &service : m_childServices) {
// qDebug() << "$$$$$$$$$$$$$$$$$$$$$ Planting CLONE effect in " << (void *)service.lock().get();
effectItem->plantClone(service);
diff -Nru kdenlive-22.04.1/src/jobs/cliploadtask.cpp kdenlive-22.04.2/src/jobs/cliploadtask.cpp
--- kdenlive-22.04.1/src/jobs/cliploadtask.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/jobs/cliploadtask.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -735,11 +735,13 @@
void ClipLoadTask::abort()
{
- Fun undo = []() { return true; };
- Fun redo = []() { return true; };
m_progress = 100;
pCore->taskManager.taskDone(m_owner.second, this);
- qDebug()<projectItemModel()->getClipByBinID(QString::number(m_owner.second))->clipUrl();
+ if (pCore->taskManager.isBlocked()) {
+ return;
+ }
+ Fun undo = []() { return true; };
+ Fun redo = []() { return true; };
QString resource = Xml::getXmlProperty(m_xml, QStringLiteral("resource"));
if (!m_softDelete) {
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
diff -Nru kdenlive-22.04.1/src/jobs/taskmanager.cpp kdenlive-22.04.2/src/jobs/taskmanager.cpp
--- kdenlive-22.04.1/src/jobs/taskmanager.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/jobs/taskmanager.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -22,6 +22,7 @@
TaskManager::TaskManager(QObject *parent)
: QObject(parent)
, m_tasksListLock(QReadWriteLock::Recursive)
+ , m_blockUpdates(false)
{
int maxThreads = qMin(4, QThread::idealThreadCount() - 1);
m_taskPool.setMaxThreadCount(qMax(maxThreads, 1));
@@ -33,6 +34,11 @@
slotCancelJobs();
}
+bool TaskManager::isBlocked() const
+{
+ return m_blockUpdates;
+}
+
void TaskManager::updateConcurrency()
{
m_transcodePool.setMaxThreadCount(KdenliveSettings::proxythreads());
@@ -124,6 +130,7 @@
{
m_tasksListLock.lockForRead();
// See if there is already a task for this MLT service and resource.
+ m_blockUpdates = true;
for (const auto &task : m_taskList) {
for (AbstractTask* t : task.second) {
// If so, then just add ourselves to be notified upon completion.
@@ -133,6 +140,7 @@
m_tasksListLock.unlock();
m_taskPool.waitForDone();
m_transcodePool.waitForDone();
+ m_blockUpdates = false;
updateJobCount();
}
diff -Nru kdenlive-22.04.1/src/jobs/taskmanager.h kdenlive-22.04.2/src/jobs/taskmanager.h
--- kdenlive-22.04.1/src/jobs/taskmanager.h 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/jobs/taskmanager.h 2022-06-12 20:11:07.000000000 +0000
@@ -63,6 +63,9 @@
/** @brief Update the number of concurrent jobs allowed */
void updateConcurrency();
+ /** @brief We are aborting all tasks and don't want them to send any updates */
+ bool isBlocked() const;
+
/** @brief return the message of a given job on a given clip (message, detailed log)*/
//QPair getJobMessageForClip(int jobId, const QString &binId) const;
@@ -79,6 +82,7 @@
QThreadPool m_transcodePool;
std::unordered_map > m_taskList;
mutable QReadWriteLock m_tasksListLock;
+ bool m_blockUpdates;
signals:
void jobCount(int);
diff -Nru kdenlive-22.04.1/src/main.cpp kdenlive-22.04.2/src/main.cpp
--- kdenlive-22.04.1/src/main.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/main.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -102,6 +102,22 @@
splash.show();
qApp->processEvents(QEventLoop::AllEvents);
+ QString packageType;
+ if (qEnvironmentVariableIsSet("PACKAGE_TYPE")) {
+ packageType = qgetenv("PACKAGE_TYPE").toLower();
+ } else {
+ // no package type defined, try to detected it
+ QString appPath = qApp->applicationDirPath();
+ if (appPath.contains(QStringLiteral("/tmp/.mount_"))) {
+ packageType = QStringLiteral("appimage");
+ }
+ if (appPath.contains(QStringLiteral("/snap"))) {
+ packageType = QStringLiteral("snap");
+ } else {
+ qDebug() << "Could not detect package type, probably default? App dir is" << qApp->applicationDirPath();
+ }
+ }
+
#ifdef Q_OS_WIN
qputenv("KDE_FORK_SLAVES", "1");
QString path = qApp->applicationDirPath() + QLatin1Char(';') + qgetenv("PATH");
@@ -125,21 +141,33 @@
}
}
}
-#endif
- QString packageType;
- if (qEnvironmentVariableIsSet("PACKAGE_TYPE")) {
- packageType = qgetenv("PACKAGE_TYPE").toLower();
- } else {
- // no package type defined, try to detected it
- QString appPath = qApp->applicationDirPath();
- if (appPath.contains(QStringLiteral("/tmp/.mount_"))) {
- packageType = QStringLiteral("appimage");
- } if (appPath.contains(QStringLiteral("/snap"))) {
- packageType = QStringLiteral("snap");
- } else {
- qDebug() << "Could not detect package type, probably default? App dir is" << qApp->applicationDirPath();
+#else
+ // AppImage
+ if (packageType == QStringLiteral("appimage")) {
+ QMap themeMap;
+ themeMap.insert("breeze", "/../icons/breeze/breeze-icons.rcc");
+ themeMap.insert("breeze-dark", "/../icons/breeze-dark/breeze-icons-dark.rcc");
+
+ QMapIterator i(themeMap);
+ while (i.hasNext()) {
+ i.next();
+ QString themePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, i.value());
+ if (!themePath.isEmpty()) {
+ const QString iconSubdir = "/icons/" + i.key();
+ if (QResource::registerResource(themePath, iconSubdir)) {
+ if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) {
+ qDebug() << "Loaded icon theme:" << i.key();
+ } else {
+ qWarning() << "No index.theme found for" << i.key();
+ QResource::unregisterResource(themePath, iconSubdir);
+ }
+ } else {
+ qWarning() << "Invalid rcc file" << i.key();
+ }
+ }
}
}
+#endif
bool inSandbox = false;
if (packageType == QStringLiteral("appimage") || packageType == QStringLiteral("flatpak") || packageType == QStringLiteral("snap")) {
@@ -153,16 +181,16 @@
KConfigGroup grp(config, "unmanaged");
if (!grp.exists()) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
- if (env.contains(QStringLiteral("XDG_CURRENT_DESKTOP")) && env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde")) {
- qCDebug(KDENLIVE_LOG) << "KDE Desktop detected, using system icons";
+ if (env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde") && packageType != QStringLiteral("appimage")) {
+ qCDebug(KDENLIVE_LOG) << "KDE Desktop detected and not Appimage, using system icons";
} else {
- // We are not on a KDE desktop, force breeze icon theme
+ // We are not on a KDE desktop or in an Appimage, force breeze icon theme
// Check if breeze theme is available
QStringList iconThemes = KIconTheme::list();
if (iconThemes.contains(QStringLiteral("breeze"))) {
grp.writeEntry("force_breeze", true);
grp.writeEntry("use_dark_breeze", true);
- qCDebug(KDENLIVE_LOG) << "Non KDE Desktop detected, forcing Breeze icon theme";
+ qCDebug(KDENLIVE_LOG) << "Non KDE Desktop or Appimage detected, forcing Breeze icon theme";
}
}
}
diff -Nru kdenlive-22.04.1/src/mainwindow.cpp kdenlive-22.04.2/src/mainwindow.cpp
--- kdenlive-22.04.1/src/mainwindow.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/mainwindow.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -893,8 +893,14 @@
KSharedConfigPtr kconfig = KSharedConfig::openConfig();
KConfigGroup initialGroup(kconfig, "version");
- if (initialGroup.exists() && KdenliveSettings::force_breeze() && useDarkIcons != KdenliveSettings::use_dark_breeze()) {
- // We need to reload icon theme
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ bool isAppimage = pCore->packageType() == QStringLiteral("appimage");
+ bool isKDE = env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde");
+ bool forceBreeze = initialGroup.exists() && KdenliveSettings::force_breeze();
+ if ((!isKDE || isAppimage || forceBreeze) &&
+ ((useDarkIcons && QIcon::themeName() == QStringLiteral("breeze")) || (!useDarkIcons && QIcon::themeName() == QStringLiteral("breeze-dark")))) {
+ // We need to reload icon theme, on KDE desktops this is not necessary, however for the Appimage it is even on KDE Desktop
+ // See also https://kate-editor.org/post/2021/2021-03-07-cross-platform-light-dark-themes-and-icons/
QIcon::setThemeName(useDarkIcons ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"));
KdenliveSettings::setUse_dark_breeze(useDarkIcons);
}
@@ -2336,8 +2342,8 @@
connect(pCore->mixer(), &MixerManager::purgeCache, m_projectMonitor, &Monitor::purgeCache);
getMainTimeline()->controller()->clipActions = kdenliveCategoryMap.value(QStringLiteral("timelineselection"))->actions();
- connect(m_projectMonitor, &Monitor::zoneUpdated, project, [&](const QPoint &) { project->setModified(); });
- connect(m_clipMonitor, &Monitor::zoneUpdated, project, [&](const QPoint &) { project->setModified(); });
+ connect(m_projectMonitor, &Monitor::zoneUpdated, project, [project](const QPoint &) { project->setModified(); });
+ connect(m_clipMonitor, &Monitor::zoneUpdated, project, [project](const QPoint &) { project->setModified(); });
connect(project, &KdenliveDoc::docModified, this, &MainWindow::slotUpdateDocumentState);
if (m_renderWidget) {
diff -Nru kdenlive-22.04.1/src/monitor/monitor.cpp kdenlive-22.04.2/src/monitor/monitor.cpp
--- kdenlive-22.04.1/src/monitor/monitor.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/monitor/monitor.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -691,7 +691,11 @@
updateGeometry();
}
-void Monitor::buildBackgroundedProducer(int pos) {
+void Monitor::buildBackgroundedProducer(int pos)
+{
+ if (m_controller == nullptr) {
+ return;
+ }
if (KdenliveSettings::monitor_background() != "black") {
Mlt::Tractor trac(pCore->getCurrentProfile()->profile());
QString color = QString("color:%1").arg(KdenliveSettings::monitor_background());
@@ -713,7 +717,6 @@
}
}
-
void Monitor::updateMarkers()
{
if (m_markerMenu) {
@@ -953,10 +956,14 @@
m_glWidget->setParent(nullptr);
m_glWidget->move(screenRect.topLeft());
m_glWidget->resize(screenRect.size());
+ screenFound = true;
break;
}
}
}
+ if (!screenFound) {
+ m_glWidget->setParent(nullptr);
+ }
} else {
m_glWidget->setParent(nullptr);
}
@@ -1294,9 +1301,9 @@
}
if (model) {
- bool found = false;
- CommentedTime marker = model->getMarker(GenTime(pos, pCore->getCurrentFps()), &found);
- if (found) {
+ int mid = model->markerIdAtFrame(pos);
+ if (mid > -1) {
+ CommentedTime marker = model->markerById(mid);
overlayText = marker.comment();
color = model->markerTypes.at(marker.markerType());
}
@@ -1629,6 +1636,9 @@
disconnect(m_controller.get(), &ProjectClip::boundsChanged, m_glMonitor->getControllerProxy(), &MonitorProxy::updateClipBounds);
disconnect(m_controller.get(), &ProjectClip::registeredClipChanged, m_controller.get(), &ProjectClip::checkClipBounds);
}
+ } else if (controller == nullptr) {
+ // Nothing to do
+ return;
}
disconnect(this, &Monitor::seekPosition, this, &Monitor::seekRemap);
m_controller = controller;
@@ -1801,11 +1811,13 @@
void Monitor::slotPreviewResource(const QString &path, const QString &title)
{
- if (!QUrl::fromUserInput(path).isLocalFile()) {
- warningMessage(i18n("It maybe takes a while until the preview is loaded"), 15000);
+ if (isPlaying()) {
+ stop();
}
+ QApplication::processEvents();
slotOpenClip(nullptr);
m_streamAction->setVisible(false);
+ // TODO: direct loading of the producer blocks UI, we should use a task to load the producer
m_glMonitor->setProducer(path);
m_timePos->setRange(0, m_glMonitor->producer()->get_length() - 1);
m_glMonitor->getControllerProxy()->setClipProperties(-1, ClipType::Unknown, false, title);
@@ -2366,7 +2378,7 @@
}
QString newComment = root->property("markerText").toString();
bool found = false;
- CommentedTime oldMarker = model->getMarker(m_timePos->gentime(), &found);
+ CommentedTime oldMarker = model->getMarker(m_timePos->getValue(), &found);
if (!found || newComment == oldMarker.comment()) {
// No change
return;
diff -Nru kdenlive-22.04.1/src/monitor/monitor.h kdenlive-22.04.2/src/monitor/monitor.h
--- kdenlive-22.04.1/src/monitor/monitor.h 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/monitor/monitor.h 2022-06-12 20:11:07.000000000 +0000
@@ -242,8 +242,6 @@
void slotForceSize(QAction *a);
void buildBackgroundedProducer(int pos);
void slotSeekToKeyFrame();
- /** @brief Display a non blocking error message to user **/
- void warningMessage(const QString &text, int timeout = 5000, const QList &actions = QList());
void slotLockMonitor(bool lock);
void slotSwitchPlay();
void slotEditInlineMarker();
@@ -288,6 +286,8 @@
void slotRewind(double speed = 0) override;
void slotRewindOneFrame(int diff = 1);
void slotForwardOneFrame(int diff = 1);
+ /** @brief Display a non blocking error message to user **/
+ void warningMessage(const QString &text, int timeout = 5000, const QList &actions = QList());
void slotStart();
/** @brief Set position and information for the trimming preview
* @param pos Absolute position in frames
diff -Nru kdenlive-22.04.1/src/monitor/monitorproxy.cpp kdenlive-22.04.2/src/monitor/monitorproxy.cpp
--- kdenlive-22.04.1/src/monitor/monitorproxy.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/monitor/monitorproxy.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -471,7 +471,7 @@
QStringList effectInfo = effectSource.split(QLatin1Char('-'));
effectInfo.prepend(effectData);
if (m_clipId > -1) {
- pCore->bin()->slotAddEffect(QString::number(m_clipId), effectInfo);
+ QMetaObject::invokeMethod(pCore->bin(), "slotAddEffect", Qt::QueuedConnection, Q_ARG(QString, QString::number(m_clipId)), Q_ARG(QStringList, effectInfo));
} else {
// Dropped in project monitor
emit addTimelineEffect(effectInfo);
diff -Nru kdenlive-22.04.1/src/onlineresources/resourcewidget.cpp kdenlive-22.04.2/src/onlineresources/resourcewidget.cpp
--- kdenlive-22.04.1/src/onlineresources/resourcewidget.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/onlineresources/resourcewidget.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -28,6 +28,7 @@
ResourceWidget::ResourceWidget(QWidget *parent)
: QWidget(parent)
+ , m_showloadingWarning(true)
{
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
setupUi(this);
@@ -463,9 +464,18 @@
if (!m_currentItem) {
return;
}
-
blockUI(true);
- emit previewClip(m_currentItem->data(previewRole).toString(), i18n("Online Resources Preview"));
+ const QString path = m_currentItem->data(previewRole).toString();
+ if (m_showloadingWarning && !QUrl::fromUserInput(path).isLocalFile()) {
+ message_line->setText(i18n("It maybe takes a while until the preview is loaded"));
+ message_line->setMessageType(KMessageWidget::Warning);
+ message_line->show();
+ QTimer::singleShot(6000, message_line, &KMessageWidget::animatedHide);
+ repaint();
+ // Only show this warning once
+ m_showloadingWarning = false;
+ }
+ emit previewClip(path, i18n("Online Resources Preview"));
blockUI(false);
}
diff -Nru kdenlive-22.04.1/src/onlineresources/resourcewidget.hpp kdenlive-22.04.2/src/onlineresources/resourcewidget.hpp
--- kdenlive-22.04.1/src/onlineresources/resourcewidget.hpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/onlineresources/resourcewidget.hpp 2022-06-12 20:11:07.000000000 +0000
@@ -66,6 +66,7 @@
/** @brief Default icon size for the views. */
QSize m_iconSize;
int wheelAccumulatedDelta;
+ bool m_showloadingWarning;
ResourceItemInfo getItemById(const QString &id);
void loadConfig();
void saveConfig();
diff -Nru kdenlive-22.04.1/src/renderpresets/renderpresetmodel.cpp kdenlive-22.04.2/src/renderpresets/renderpresetmodel.cpp
--- kdenlive-22.04.1/src/renderpresets/renderpresetmodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/renderpresets/renderpresetmodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -275,14 +275,14 @@
return m_aQualities.split(QLatin1Char(','), Qt::SkipEmptyParts);
#endif
} else {
- //int aq = ui->audioQualitySpinner->value();
+ // ATTENTION: historically qualities are sorted from best to worse for some reason
QString acodec = getParam(QStringLiteral("acodec")).toLower();
if (acodec == "libmp3lame") {
- return {"9", "0"};
- } else if (acodec == "libvorbis" || acodec == "vorbis") {
- return {"0", "10"};
+ return {"0", "9"};
+ } else if (acodec == "libvorbis" || acodec == "vorbis" || acodec == "libopus") {
+ return {"10", "0"};
} else {
- return {"0", "500"};
+ return {"500", "0"};
}
}
}
@@ -315,15 +315,16 @@
return m_vQualities.split(QLatin1Char(','), Qt::SkipEmptyParts);
#endif
} else {
+ // ATTENTION: historically qualities are sorted from best to worse for some reason
QString vcodec = getParam(QStringLiteral("vcodec")).toLower();
if (vcodec == "libx265" || vcodec.contains("nvenc") || vcodec.endsWith("_amf") || vcodec.startsWith("libx264") || vcodec.endsWith("_vaapi") || vcodec.endsWith("_qsv")) {
- return {"51", "0"};
+ return {"0", "51"};
} else if (vcodec.startsWith("libvpx") || vcodec.startsWith("libaom-")) {
- return {"63", "0"};
+ return {"0", "63"};
} else if (vcodec.startsWith("libwebp")) {
- return {"0", "100"};
+ return {"100", "0"};
} else {
- return {"31", "1"};
+ return {"1", "31"};
}
}
}
diff -Nru kdenlive-22.04.1/src/timeline2/model/clipmodel.cpp kdenlive-22.04.2/src/timeline2/model/clipmodel.cpp
--- kdenlive-22.04.1/src/timeline2/model/clipmodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/timeline2/model/clipmodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -1168,8 +1168,10 @@
}
}
- if (finalMove && tid != -1 && m_lastTrackId != m_currentTrackId) {
- refreshProducerFromBin(m_currentTrackId);
+ if (finalMove && m_lastTrackId != m_currentTrackId) {
+ if (tid != -1) {
+ refreshProducerFromBin(m_currentTrackId);
+ }
m_lastTrackId = m_currentTrackId;
}
}
diff -Nru kdenlive-22.04.1/src/timeline2/model/timelinemodel.cpp kdenlive-22.04.2/src/timeline2/model/timelinemodel.cpp
--- kdenlive-22.04.1/src/timeline2/model/timelinemodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/timeline2/model/timelinemodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -841,7 +841,7 @@
} else {
}
}
- ok = ok && getTrackById(trackId)->requestClipInsertion(clipId, position, updateView, finalMove, local_undo, local_redo, groupMove, false, allowedClipMixes);
+ ok = ok && getTrackById(trackId)->requestClipInsertion(clipId, position, updateView, finalMove, local_undo, local_redo, groupMove, old_trackId == -1, allowedClipMixes);
if (!ok) {
qWarning() << "clip insertion failed";
@@ -937,12 +937,10 @@
continue;
}
// Make sure we have enough space in clip to resize
- int maxLengthLeft = m_allClips[previousClip]->getMaxDuration();
- int maxLengthRight = m_allClips[s]->getMaxDuration();
// leftMax is the maximum frames we have to expand first clip on the right
- leftMax = maxLengthLeft > -1 ? (maxLengthLeft - 1 - m_allClips[previousClip]->getOut()) : m_allClips[s]->getPlaytime();
+ leftMax = m_allClips[s]->getPlaytime();
// rightMax is the maximum frames we have to expand second clip on the left
- rightMax = maxLengthRight > -1 ? (m_allClips[s]->getIn()) : m_allClips[previousClip]->getPlaytime();
+ rightMax = m_allClips[previousClip]->getPlaytime();
if (getTrackById_const(selectedTrack)->hasStartMix(previousClip)) {
int spaceBeforeMix = m_allClips[s]->getPosition() - (m_allClips[previousClip]->getPosition() + m_allClips[previousClip]->getMixDuration());
rightMax = rightMax == -1 ? spaceBeforeMix : qMin(rightMax, spaceBeforeMix);
@@ -966,12 +964,10 @@
} else {
// Mix at end of selected clip
// Make sure we have enough space in clip to resize
- int maxLengthLeft = m_allClips[s]->getMaxDuration();
- int maxLengthRight = m_allClips[nextClip]->getMaxDuration();
// leftMax is the maximum frames we have to expand first clip on the right
- leftMax = maxLengthLeft > -1 ? (maxLengthLeft - 1 - m_allClips[s]->getOut()) : m_allClips[nextClip]->getPlaytime();
+ leftMax = m_allClips[nextClip]->getPlaytime();
// rightMax is the maximum frames we have to expand second clip on the left
- rightMax = maxLengthRight > -1 ? (m_allClips[nextClip]->getIn()) : m_allClips[s]->getPlaytime();
+ rightMax = m_allClips[s]->getPlaytime();
if (getTrackById_const(selectedTrack)->hasStartMix(s)) {
int spaceBeforeMix = m_allClips[nextClip]->getPosition() - (m_allClips[s]->getPosition() + m_allClips[s]->getMixDuration());
rightMax = rightMax == -1 ? spaceBeforeMix : qMin(rightMax, spaceBeforeMix);
@@ -1010,6 +1006,7 @@
if (rightMax > -1) {
// Both clips have limited durations
mixDurations.first = qMin(mixDuration / 2, leftMax);
+ mixDurations.first = qMin(mixDurations.first, leftMax);
mixDurations.second = qMin(mixDuration - mixDuration / 2, rightMax);
int offset = mixDuration - (mixDurations.first + mixDurations.second);
if (offset > 0) {
@@ -2207,6 +2204,46 @@
}
}
bool trackChanged = false;
+ if (delta_track != 0) {
+ //Ensure the track move is possible (not outside our current tracks)
+ for (int item : all_items) {
+ int current_track_id = old_track_ids[item];
+ int current_track_position = getTrackPosition(current_track_id);
+ bool audioTrack = getTrackById_const(current_track_id)->isAudioTrack();
+ int d = audioTrack ? audio_delta : video_delta;
+ int target_track_position = current_track_position + d;
+ bool brokenMove = target_track_position < 0 || target_track_position >= getTracksCount();
+ if (!brokenMove) {
+ int target_id = getTrackIndexFromPosition(target_track_position);
+ brokenMove = audioTrack != getTrackById_const(target_id)->isAudioTrack();
+ }
+ if (brokenMove) {
+ if (isClip(item)) {
+ int lastTid = m_allClips[item]->getFakeTrackId();
+ int originalTid = m_allClips[item]->getCurrentTrackId();
+ int last_position = getTrackPosition(lastTid);
+ int original_position = getTrackPosition(originalTid);
+ int lastDelta = last_position - original_position;
+ if (audioTrack) {
+ if (qAbs(audio_delta) > qAbs(lastDelta)) {
+ audio_delta = lastDelta;
+ }
+ if (video_delta != 0) {
+ video_delta = - lastDelta;
+ }
+ } else {
+ if (qAbs(video_delta) > qAbs(lastDelta)) {
+ video_delta = lastDelta;
+ }
+ if (audio_delta != 0) {
+ audio_delta = - lastDelta;
+ }
+ }
+ };
+ }
+ }
+ }
+
// Reverse sort. We need to insert from left to right to avoid confusing the view
for (int item : all_items) {
@@ -6004,7 +6041,33 @@
bool res = requestCompositionDeletion(cid, undo, redo);
int newId = -1;
- res = res && requestCompositionInsertion(compoId, currentTrack, a_track, currentPos, duration, nullptr, newId, undo, redo);
+ // Check if composition should be reversed (top clip at beginning, bottom at end)
+ int topClip = getTrackById_const(currentTrack)->getClipByPosition(currentPos);
+ int bottomTid = getTrackIndexFromPosition(a_track - 1);
+ int bottomClip = -1;
+ if (bottomTid > -1) {
+ bottomClip = getTrackById_const(bottomTid)->getClipByPosition(currentPos);
+ }
+ bool reverse = false;
+ if (topClip > -1 && bottomClip > -1) {
+ if (getClipPosition(topClip) + getClipPlaytime(topClip) < getClipPosition(bottomClip) + getClipPlaytime(bottomClip)) {
+ reverse = true;
+ }
+ }
+ std::unique_ptr props(nullptr);
+ if (reverse) {
+ props = std::make_unique();
+ if (compoId == QLatin1String("dissolve")) {
+ props->set("reverse", 1);
+ } else if (compoId == QLatin1String("composite")) {
+ props->set("invert", 1);
+ } else if (compoId == QLatin1String("wipe")) {
+ props->set("geometry", "0=0% 0% 100% 100% 100%;-1=0% 0% 100% 100% 0%");
+ } else if (compoId == QLatin1String("slide")) {
+ props->set("rect", "0=0% 0% 100% 100% 100%;-1=100% 0% 100% 100% 100%");
+ }
+ }
+ res = res && requestCompositionInsertion(compoId, currentTrack, a_track, currentPos, duration, std::move(props), newId, undo, redo);
if (res) {
if (forcedTrack > -1 && isComposition(newId)) {
m_allCompositions[newId]->setForceTrack(true);
diff -Nru kdenlive-22.04.1/src/timeline2/model/trackmodel.cpp kdenlive-22.04.2/src/timeline2/model/trackmodel.cpp
--- kdenlive-22.04.1/src/timeline2/model/trackmodel.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/timeline2/model/trackmodel.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -1808,11 +1808,14 @@
if (remixPlaylists && source_track != dest_track) {
// A list of clip ids x playlists
QMap rearrangedPlaylists;
+ QMap>> mixParameters;
int ix = 0;
int moveId = m_mixList.value(clipIds.second, -1);
while (moveId > -1) {
int current = m_allClips[moveId]->getSubPlaylistIndex();
rearrangedPlaylists.insert(moveId, current);
+ QVector> params = m_sameCompositions.at(moveId)->getAllParameters();
+ mixParameters.insert(moveId, params);
if (hasEndMix(moveId)) {
moveId = m_mixList.value(moveId, -1);
} else {
@@ -1820,7 +1823,6 @@
}
ix++;
}
-
rearrange_playlists = [this, rearrangedPlaylists]() {
// First, remove all clips on playlist 0
QMapIterator i(rearrangedPlaylists);
@@ -1876,7 +1878,8 @@
if (m_sameCompositions.count(i.key()) > 0) {
// There is a mix at clip start, adjust direction
Mlt::Transition &transition = *static_cast(m_sameCompositions[i.key()]->getAsset());
- transition.set("reverse", i.value());
+ bool reverse = i.value() == 1;
+ updateCompositionDirection(transition, reverse);
}
}
return true;
@@ -1884,7 +1887,7 @@
return false;
}
};
- rearrange_playlists_undo = [this, rearrangedPlaylists]() {
+ rearrange_playlists_undo = [this, rearrangedPlaylists, mixParameters]() {
// First, remove all clips on playlist 1
QMapIterator i(rearrangedPlaylists);
while (i.hasNext()) {
@@ -1939,7 +1942,13 @@
if (m_sameCompositions.count(i.key()) > 0) {
// There is a mix at clip start, adjust direction
Mlt::Transition &transition = *static_cast(m_sameCompositions[i.key()]->getAsset());
- transition.set("reverse", 1 - i.value());
+ if (mixParameters.contains(i.key())) {
+ // Restore all original params
+ QVector> params = mixParameters.value(i.key());
+ for (const auto &p : qAsConst(params)) {
+ transition.set(p.first.toUtf8().constData(), p.second.toString().toUtf8().constData());
+ }
+ }
}
}
return true;
@@ -1962,6 +1971,9 @@
t->set("kdenlive:mixcut", secondClipCut);
t->set("start", -1);
t->set("accepts_blanks", 1);
+ if (dest_track == 0) {
+ t->set("reverse", 1);
+ }
m_track->plant_transition(*t.get(), 0, 1);
assetName = QStringLiteral("mix");
xml = TransitionsRepository::get()->getXml(assetName);
@@ -1972,13 +1984,14 @@
xml = TransitionsRepository::get()->getXml(assetName);
t->set("kdenlive:mixcut", secondClipCut);
t->set("kdenlive_id", "luma");
- m_track->plant_transition(*t.get(), 0, 1);
if (dest_track == 0) {
t->set("reverse", 1);
}
+ m_track->plant_transition(*t.get(), 0, 1);
}
- if (dest_track == 0 && Xml::hasXmlParameter(xml, QStringLiteral("reverse"))) {
- Xml::setXmlParameter(xml, QStringLiteral("reverse"), QStringLiteral("1"));
+ if (dest_track == 0) {
+ // Mix should be reversed
+ reverseCompositionXml(mixId, xml);
}
std::shared_ptr asset(new AssetParameterModel(std::move(t), xml, assetName, {ObjectType::TimelineMix, clipIds.second}, QString()));
m_sameCompositions[clipIds.second] = asset;
@@ -2544,7 +2557,12 @@
// First remove existing mix
// lock MLT playlist so that we don't end up with invalid frames in monitor
const QString currentAsset = m_sameCompositions[cid]->getAssetId();
- Fun local_redo = [this, cid, composition]() {
+ QVector> allParams = m_sameCompositions[cid]->getAllParameters();
+ // Check if mix should be reversed
+
+ bool reverse = m_allClips[cid]->getSubPlaylistIndex() == 0;
+ // TODO: handle revert mixes
+ Fun local_redo = [this, cid, composition, reverse]() {
m_playlists[0].lock();
m_playlists[1].lock();
Mlt::Transition &transition = *static_cast(m_sameCompositions[cid]->getAsset());
@@ -2562,6 +2580,10 @@
m_track->plant_transition(*t.get(), 0, 1);
t->set("kdenlive:mixcut", mixCutPos);
QDomElement xml = TransitionsRepository::get()->getXml(composition);
+ if (reverse) {
+ // Mix should be reversed
+ reverseCompositionXml(composition, xml);
+ }
std::shared_ptr asset(new AssetParameterModel(std::move(t), xml, composition, {ObjectType::TimelineMix, cid}, QString()));
m_sameCompositions[cid] = asset;
}
@@ -2569,7 +2591,7 @@
m_playlists[1].unlock();
return true;
};
- Fun local_undo = [this, cid, currentAsset]() {
+ Fun local_undo = [this, cid, currentAsset, allParams]() {
m_playlists[0].lock();
m_playlists[1].lock();
Mlt::Transition &transition = *static_cast(m_sameCompositions[cid]->getAsset());
@@ -2585,6 +2607,17 @@
t->set_in_and_out(in, out);
m_track->plant_transition(*t.get(), 0, 1);
QDomElement xml = TransitionsRepository::get()->getXml(currentAsset);
+ QDomNodeList xmlParams = xml.elementsByTagName(QStringLiteral("parameter"));
+ for (int i = 0; i < xmlParams.count(); ++i) {
+ QDomElement currentParameter = xmlParams.item(i).toElement();
+ QString paramName = currentParameter.attribute(QStringLiteral("name"));
+ for (const auto &p : qAsConst(allParams)) {
+ if (p.first == paramName) {
+ currentParameter.setAttribute(QStringLiteral("value"), p.second.toString());
+ break;
+ }
+ }
+ }
std::shared_ptr asset(new AssetParameterModel(std::move(t), xml, currentAsset, {ObjectType::TimelineMix, cid}, QString()));
m_sameCompositions[cid] = asset;
}
@@ -2595,6 +2628,116 @@
UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
}
+void TrackModel::reverseCompositionXml(const QString &composition, QDomElement xml)
+{
+ if (composition == QLatin1String("luma") || composition == QLatin1String("dissolve") || composition == QLatin1String("mix")) {
+ Xml::setXmlParameter(xml, QStringLiteral("reverse"), QStringLiteral("1"));
+ } else if (composition == QLatin1String("composite")) {
+ Xml::setXmlParameter(xml, QStringLiteral("invert"), QStringLiteral("1"));
+ } else if (composition == QLatin1String("wipe")) {
+ Xml::setXmlParameter(xml, QStringLiteral("geometry"), QStringLiteral("0=0% 0% 100% 100% 100%;-1=0% 0% 100% 100% 0%"));
+ } else if (composition == QLatin1String("slide")) {
+ Xml::setXmlParameter(xml, QStringLiteral("rect"), QStringLiteral("0=0% 0% 100% 100% 100%;-1=100% 0% 100% 100% 100%"));
+ }
+}
+
+void TrackModel::updateCompositionDirection(Mlt::Transition &transition, bool reverse)
+{
+ QString composition(transition.get("kdenlive_id"));
+ if (composition.isEmpty()) {
+ composition = transition.get("mlt_service");
+ }
+ if (composition == QLatin1String("luma") || composition == QLatin1String("dissolve") || composition == QLatin1String("mix")) {
+ transition.set("reverse", reverse ? 1 : 0);
+ } else if (composition == QLatin1String("composite")) {
+ transition.set("invert", reverse ? 1 : 0);
+ } else if (composition == QLatin1String("wipe")) {
+ if (reverse) {
+ transition.set("geometry", "0=0% 0% 100% 100% 100%;-1=0% 0% 100% 100% 0%");
+ } else {
+ transition.set("geometry", "0=0% 0% 100% 100% 0%;-1=0% 0% 100% 100% 100%");
+ }
+ } else if (composition == QLatin1String("slide")) {
+ QString currentSlide(transition.get("rect"));
+ currentSlide.replace(QLatin1Char('%'), QString());
+ currentSlide = currentSlide.section(QLatin1Char('='), 1);
+ // Check if we start centered
+ if (!reverse && currentSlide.startsWith(QLatin1String("0 0 "))) {
+ currentSlide = currentSlide.section(QLatin1Char('='), 1);
+ QStringList sizes = currentSlide.split(QLatin1Char(' '));
+ double x = sizes.at(0).toDouble();
+ double y = sizes.at(1).toDouble();
+ QString result = QStringLiteral("0=");
+ if (x > 0) {
+ result.append(QStringLiteral("-100% "));
+ } else if (x < 0) {
+ result.append(QStringLiteral("100% "));
+ } else {
+ result.append(QStringLiteral("0% "));
+ }
+ if (y > 0) {
+ result.append(QStringLiteral("-100% "));
+ } else if (y < 0) {
+ result.append(QStringLiteral("100% "));
+ } else {
+ result.append(QStringLiteral("0% "));
+ }
+ result.append(QStringLiteral("100% 100% 100%;-1=0% 0% 100% 100% 100%"));
+ transition.set("rect", result.toUtf8().constData());
+ } else if (reverse) {
+ QString secondPart = currentSlide.section(QLatin1Char('='), 1);
+ if (secondPart.startsWith(QLatin1String("0 0 "))) {
+ QStringList sizes = currentSlide.split(QLatin1Char(' '));
+ double x = sizes.at(0).toDouble();
+ double y = sizes.at(1).toDouble();
+ QString result = QStringLiteral("0=0% 0% 100% 100% 100%;-1=");
+ if (x > 0) {
+ result.append(QStringLiteral("-100% "));
+ } else if (x < 0) {
+ result.append(QStringLiteral("100% "));
+ } else {
+ result.append(QStringLiteral("0% "));
+ }
+ if (y > 0) {
+ result.append(QStringLiteral("-100% "));
+ } else if (y < 0) {
+ result.append(QStringLiteral("100% "));
+ } else {
+ result.append(QStringLiteral("0% "));
+ }
+ result.append(QStringLiteral("100% 100% 100%"));
+ transition.set("rect", result.toUtf8().constData());
+ }
+ }
+ }
+}
+
+bool TrackModel::mixIsReversed(int cid) const
+{
+ if (m_sameCompositions.count(cid) > 0) {
+ // There is a mix at clip start, adjust direction
+ Mlt::Transition &transition = *static_cast(m_sameCompositions.at(cid)->getAsset());
+ QString composition(transition.get("kdenlive_id"));
+ if (composition.isEmpty()) {
+ composition = transition.get("mlt_service");
+ }
+ if (composition == QLatin1String("luma") || composition == QLatin1String("dissolve") || composition == QLatin1String("mix")) {
+ return transition.get_int("reverse") == 1;
+ } else if (composition == QLatin1String("composite")) {
+ return transition.get_int("invert") == 1;
+ } else if (composition == QLatin1String("wipe")) {
+ QString geom = transition.get("geometry");
+ geom.replace(QLatin1Char('%'), QString());
+ return geom.contains(QStringLiteral(" 100;"));
+ } else if (composition == QLatin1String("slide")) {
+ QString geom(transition.get("rect"));
+ geom.replace(QLatin1Char('%'), QString());
+ return geom.startsWith(QStringLiteral("0=0 0 "));
+ }
+ }
+ return false;
+}
+
QVariantList TrackModel::stackZones() const
{
return m_effectStack->getEffectZones();
diff -Nru kdenlive-22.04.1/src/timeline2/model/trackmodel.hpp kdenlive-22.04.2/src/timeline2/model/trackmodel.hpp
--- kdenlive-22.04.1/src/timeline2/model/trackmodel.hpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/timeline2/model/trackmodel.hpp 2022-06-12 20:11:07.000000000 +0000
@@ -330,6 +330,8 @@
int isOnCut(int cid);
/** @brief Returns all mix info as xml */
QDomElement mixXml(QDomDocument &document, int cid) const;
+ /** @brief Check if a mix is reversed (moslty used in tests) */
+ bool mixIsReversed(int cid) const;
public slots:
/** Delete the current track and all its associated clips */
@@ -359,6 +361,8 @@
/// This is a lock that ensures safety in case of concurrent access
mutable QReadWriteLock m_lock;
+ void reverseCompositionXml(const QString &composition, QDomElement xml);
+ void updateCompositionDirection(Mlt::Transition &transition, bool reverse);
protected:
std::shared_ptr m_effectStack;
diff -Nru kdenlive-22.04.1/src/timeline2/view/qml/Clip.qml kdenlive-22.04.2/src/timeline2/view/qml/Clip.qml
--- kdenlive-22.04.1/src/timeline2/view/qml/Clip.qml 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/timeline2/view/qml/Clip.qml 2022-06-12 20:11:07.000000000 +0000
@@ -169,6 +169,10 @@
}
clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
clipRoot.height = Logic.getTrackById(clipRoot.fakeTid).height
+ } else {
+ clipRoot.height = Qt.binding(function () {
+ return parentTrack.height
+ })
}
}
diff -Nru kdenlive-22.04.1/src/timeline2/view/timelinecontroller.cpp kdenlive-22.04.2/src/timeline2/view/timelinecontroller.cpp
--- kdenlive-22.04.1/src/timeline2/view/timelinecontroller.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/timeline2/view/timelinecontroller.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -447,6 +447,7 @@
int TimelineController::insertNewCompositionAtPos(int tid, int position, const QString &transitionId)
{
+ // TODO: adjust position and duration to existing clips ?
return insertComposition(tid, position, transitionId, true);
}
@@ -517,10 +518,12 @@
props = std::make_unique();
if (transitionId == QLatin1String("dissolve")) {
props->set("reverse", 1);
- } else if (transitionId == QLatin1String("composite") || transitionId == QLatin1String("slide")) {
+ } else if (transitionId == QLatin1String("composite")) {
props->set("invert", 1);
} else if (transitionId == QLatin1String("wipe")) {
props->set("geometry", "0=0% 0% 100% 100% 100%;-1=0% 0% 100% 100% 0%");
+ } else if (transitionId == QLatin1String("slide")) {
+ props->set("rect", "0=0% 0% 100% 100% 100%;-1=100% 0% 100% 100% 100%");
}
}
if (!m_model->requestCompositionInsertion(transitionId, tid, position, duration, std::move(props), id, logUndo)) {
@@ -541,7 +544,34 @@
{
int id;
int duration = pCore->getDurationFromString(KdenliveSettings::transition_duration());
- if (!m_model->requestCompositionInsertion(transitionId, tid, position, duration, nullptr, id, logUndo)) {
+ // Check if composition should be reversed (top clip at beginning, bottom at end)
+ int a_track = m_model->getPreviousVideoTrackPos(tid);
+ int topClip = m_model->getTrackById_const(tid)->getClipByPosition(position);
+ int bottomTid = m_model->getTrackIndexFromPosition(a_track - 1);
+ int bottomClip = -1;
+ if (bottomTid > -1) {
+ bottomClip = m_model->getTrackById_const(bottomTid)->getClipByPosition(position);
+ }
+ bool reverse = false;
+ if (topClip > -1 && bottomClip > -1) {
+ if (m_model->getClipPosition(topClip) + m_model->getClipPlaytime(topClip) < m_model->getClipPosition(bottomClip) + m_model->getClipPlaytime(bottomClip)) {
+ reverse = true;
+ }
+ }
+ std::unique_ptr props(nullptr);
+ if (reverse) {
+ props = std::make_unique();
+ if (transitionId == QLatin1String("dissolve")) {
+ props->set("reverse", 1);
+ } else if (transitionId == QLatin1String("composite")) {
+ props->set("invert", 1);
+ } else if (transitionId == QLatin1String("wipe")) {
+ props->set("geometry", "0=0% 0% 100% 100% 100%;-1=0% 0% 100% 100% 0%");
+ } else if (transitionId == QLatin1String("slide")) {
+ props->set("rect", "0=0% 0% 100% 100% 100%;-1=100% 0% 100% 100% 100%");
+ }
+ }
+ if (!m_model->requestCompositionInsertion(transitionId, tid, position, duration, std::move(props), id, logUndo)) {
id = -1;
}
return id;
@@ -1272,7 +1302,7 @@
if (frame == -1) {
frame = pCore->getTimelinePosition();
}
- CommentedTime marker = pCore->currentDoc()->getGuideModel()->getMarker(GenTime(frame, pCore->getCurrentFps()), &markerFound);
+ CommentedTime marker = pCore->currentDoc()->getGuideModel()->getMarker(frame, &markerFound);
if (!markerFound) {
if (deleteOnly) {
pCore->displayMessage(i18n("No guide found at current position"), ErrorMessage, 500);
@@ -3943,6 +3973,26 @@
int trackId = m_model->m_allClips[clipId]->getFakeTrackId();
if (m_model->getClipPosition(clipId) == position && m_model->getClipTrackId(clipId) == trackId) {
qDebug() << "* * ** END FAKE; NO MOVE RQSTED";
+ // Ensure clip height binds again with parent track height
+ if (m_model->m_groups->isInGroup(clipId)) {
+ int groupId = m_model->m_groups->getRootId(clipId);
+ auto all_items = m_model->m_groups->getLeaves(groupId);
+ for (int item : all_items) {
+ if (m_model->isClip(item)) {
+ m_model->m_allClips[item]->setFakeTrackId(-1);
+ QModelIndex modelIndex = m_model->makeClipIndexFromID(item);
+ if (modelIndex.isValid()) {
+ m_model->notifyChange(modelIndex, modelIndex, TimelineModel::FakeTrackIdRole);
+ }
+ }
+ }
+ } else {
+ m_model->m_allClips[clipId]->setFakeTrackId(-1);
+ QModelIndex modelIndex = m_model->makeClipIndexFromID(clipId);
+ if (modelIndex.isValid()) {
+ m_model->notifyChange(modelIndex, modelIndex, TimelineModel::FakeTrackIdRole);
+ }
+ }
return true;
}
if (m_model->m_groups->isInGroup(clipId)) {
diff -Nru kdenlive-22.04.1/src/ui/renderwidget_ui.ui kdenlive-22.04.2/src/ui/renderwidget_ui.ui
--- kdenlive-22.04.1/src/ui/renderwidget_ui.ui 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/ui/renderwidget_ui.ui 2022-06-12 20:11:07.000000000 +0000
@@ -6,14 +6,14 @@
0
0
- 1011
- 795
+ 900
+ 900
Rendering
-
+
0
@@ -417,9 +417,6 @@
0
-
- QComboBox::AdjustToMinimumContentsLength
-
@@ -429,348 +426,370 @@
-
-
-
-
-
-
-
- Video
-
-
- true
-
-
-
-
-
-
- Render at Preview Resolution
-
-
-
- -
-
-
- Use Proxy Clips
-
-
-
- -
-
-
- Enabled
-
-
-
- -
-
-
- Rescale:
-
-
-
- -
-
-
-
-
-
- 1
-
-
- 10000
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- x
-
-
-
- -
-
-
- 1
-
-
- 10000
-
-
-
- -
-
-
- Preserve aspect ratio
-
-
- ...
-
-
-
- ..
-
-
- true
-
-
- false
-
-
- true
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
- true
-
-
-
- -
-
-
- Overlay:
-
-
-
-
-
-
- -
-
-
- Audio
-
-
- true
-
-
-
-
-
-
- Separate file for each audio track
-
-
-
-
-
-
- -
-
-
- Custom Quality
-
-
- true
-
-
-
-
-
-
-
-
-
- Low
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Compromise file size versus quality.
-
-
- 0
-
-
- 4
-
-
- 3
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- 75%
-
-
-
- -
-
-
- High
-
-
-
-
-
-
-
-
- -
-
-
- Encoder
-
-
-
-
-
-
- For a given quality, tune the compromise between encoding time and output file size (faster encoding ends with larger file).
-
-
- Speed:
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- For a given quality, tune the compromise between encoding time and output file size (faster encoding ends with larger file).
-
-
- 1
-
-
- 2
-
-
- 1
-
-
- Qt::Horizontal
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Encoding threads
-
-
- Auto
-
-
-
- -
-
-
- Threads:
-
-
-
- -
-
-
- Parallel Processing (experimental!)
-
-
-
-
-
-
- -
-
-
- 2 pass
-
-
-
- -
-
-
- Export metadata
-
-
-
- -
-
-
- Open browser window after export
-
-
-
- -
-
-
- Play after render
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- true
-
-
- false
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
+
+
+ QFrame::NoFrame
+
+
+ Qt::ScrollBarAsNeeded
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 334
+ 855
+
+
+
+ -
+
+
+ Video
+
+
+ true
+
+
+
-
+
+
+ Render at Preview Resolution
+
+
+
+ -
+
+
+ Use Proxy Clips
+
+
+
+ -
+
+
+ Enabled
+
+
+
+ -
+
+
+ Rescale:
+
+
+
+ -
+
+
-
+
+
+ 1
+
+
+ 10000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ x
+
+
+
+ -
+
+
+ 1
+
+
+ 10000
+
+
+
+ -
+
+
+ Preserve aspect ratio
+
+
+ ...
+
+
+
+ ..
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Overlay:
+
+
+
+
+
+
+ -
+
+
+ Audio
+
+
+ true
+
+
+
-
+
+
+ Separate file for each audio track
+
+
+
+
+
+
+ -
+
+
+ Custom Quality
+
+
+ true
+
+
+
-
+
+
-
+
+
+ Low
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Compromise file size versus quality.
+
+
+ 0
+
+
+ 4
+
+
+ 3
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 75%
+
+
+
+ -
+
+
+ High
+
+
+
+
+
+
+
+
+ -
+
+
+ Encoder
+
+
+
-
+
+
+ For a given quality, tune the compromise between encoding time and output file size (faster encoding ends with larger file).
+
+
+ Speed:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ For a given quality, tune the compromise between encoding time and output file size (faster encoding ends with larger file).
+
+
+ 1
+
+
+ 2
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Encoding threads
+
+
+ Auto
+
+
+
+ -
+
+
+ Threads:
+
+
+
+ -
+
+
+ Parallel Processing (experimental!)
+
+
+
+
+
+
+ -
+
+
+ 2 pass
+
+
+
+ -
+
+
+ Export metadata
+
+
+
+ -
+
+
+ Open browser window after export
+
+
+
+ -
+
+
+ Play after render
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
@@ -1013,11 +1032,13 @@
KMessageWidget
QFrame
+ 1
KSeparator
QFrame
+ 1
diff -Nru kdenlive-22.04.1/src/ui/resourcewidget_ui.ui kdenlive-22.04.2/src/ui/resourcewidget_ui.ui
--- kdenlive-22.04.1/src/ui/resourcewidget_ui.ui 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/src/ui/resourcewidget_ui.ui 2022-06-12 20:11:07.000000000 +0000
@@ -6,11 +6,24 @@
0
0
- 507
- 457
+ 313
+ 335
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 81
+ 20
+
+
+
+
-
@@ -191,42 +204,38 @@
- -
-
-
-
- 0
- 0
-
+
-
+
+
+ ...
-
- false
+
+
+ ..
-
- false
+
+ true
- -
-
-
-
- 40
- 0
-
+
-
+
+
+
+ 0
+ 0
+
-
-
- 100
- 16777215
-
+
+ Import
-
- Qt::Horizontal
+
+
+ ..
- -
+
-
@@ -243,51 +252,26 @@
- -
-
-
- ...
-
-
-
- ..
-
-
- true
-
-
-
- -
-
-
- Qt::Horizontal
-
-
+
-
+
+
- 81
- 20
+ 40
+ 0
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Import
+
+
+ 100
+ 16777215
+
-
-
- ..
+
+ Qt::Horizontal
- -
+
-
...
@@ -345,15 +329,31 @@
+ -
+
+
+
+ 0
+ 0
+
+
+
+ false
+
+
+ false
+
+
+
splitter
service_box_2
- message_line
button_preview
button_import
button_zoomin
button_zoomout
slider_zoom
+ message_line
diff -Nru kdenlive-22.04.1/tests/mixtest.cpp kdenlive-22.04.2/tests/mixtest.cpp
--- kdenlive-22.04.1/tests/mixtest.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/tests/mixtest.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -60,6 +60,7 @@
int cid2;
int cid3;
int cid4;
+ int cid5;
REQUIRE(timeline->requestClipInsertion(binId, tid2, 100, cid1));
REQUIRE(timeline->requestItemResize(cid1, 10, true, true));
@@ -89,6 +90,23 @@
REQUIRE(timeline->getTrackById_const(tid1)->mixCount() == 0);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 0);
};
+
+ auto state0b = [&]() {
+ REQUIRE(timeline->getClipsCount() == 8);
+ REQUIRE(timeline->getClipPlaytime(cid1) == 10);
+ REQUIRE(timeline->getClipPosition(cid1) == 100);
+ REQUIRE(timeline->getClipPlaytime(cid2) == 10);
+ REQUIRE(timeline->getClipPosition(cid2) == 110);
+ REQUIRE(timeline->getClipPlaytime(cid5) == 10);
+ REQUIRE(timeline->getClipPosition(cid5) == 120);
+ REQUIRE(timeline->getClipPosition(cid3) == 500);
+ REQUIRE(timeline->getClipPlaytime(cid3) == 20);
+ REQUIRE(timeline->getClipPosition(cid4) == 520);
+ REQUIRE(timeline->getClipPlaytime(cid4) == 20);
+ REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->getTrackById_const(tid1)->mixCount() == 0);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 0);
+ };
auto state1 = [&]() {
REQUIRE(timeline->getClipsCount() == 6);
@@ -99,6 +117,18 @@
REQUIRE(timeline->getTrackById_const(tid3)->mixCount() == 1);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 1);
};
+
+ auto state1b = [&]() {
+ REQUIRE(timeline->getClipsCount() == 8);
+ REQUIRE(timeline->getClipPlaytime(cid1) > 10);
+ REQUIRE(timeline->getClipPosition(cid1) == 100);
+ REQUIRE(timeline->getClipPlaytime(cid2) > 10);
+ REQUIRE(timeline->getClipPosition(cid2) < 110);
+ REQUIRE(timeline->getClipPlaytime(cid5) == 10);
+ REQUIRE(timeline->getClipPosition(cid5) == 120);
+ REQUIRE(timeline->getTrackById_const(tid3)->mixCount() == 1);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 1);
+ };
auto state3 = [&, mixDuration]() {
REQUIRE(timeline->getClipsCount() == 6);
@@ -206,7 +236,7 @@
{
state0();
// insert third color clip
- int cid5;
+ cid5 = -1;
REQUIRE(timeline->requestClipInsertion(binId2, tid2, 540, cid5));
REQUIRE(timeline->requestItemResize(cid5, 20, true, true));
REQUIRE(timeline->getClipPosition(cid5) == 540);
@@ -333,6 +363,57 @@
undoStack->undo();
state0();
}
+
+ SECTION("Create chained mixes on AV clips")
+ {
+ // CID 1 length=10, pos=100, CID2 length=10, pos=110
+ // Default mix duration = 25 frames (12 before / 13 after)
+ // Resize CID2 so that it has some space to expand left
+ REQUIRE(timeline->requestItemResize(cid2, 30, true, true) == 30);
+ REQUIRE(timeline->requestItemResize(cid2, 10, false, true) == 10);
+ REQUIRE(timeline->requestClipMove(cid2, tid2, 110));
+ state0();
+
+ // Create a third AV clip and make some space
+ cid5 = -1;
+ REQUIRE(timeline->requestClipInsertion(binId, tid2, 120, cid5));
+ REQUIRE(timeline->requestItemResize(cid5, 30, true, true) == 30);
+ REQUIRE(timeline->requestItemResize(cid5, 10, false, true) == 10);
+ REQUIRE(timeline->requestClipMove(cid5, tid2, 120));
+
+ state0b();
+
+ // CID 1 length=10, pos=100, CID2 length=20, pos=130, CID5 length=20, pos=130
+
+ // Create mix between cid1 and cid2
+ REQUIRE(timeline->mixClip(cid2));
+ state1b();
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid2) == false);
+ int audio2 = timeline->getClipSplitPartner(cid2);
+ REQUIRE(timeline->getTrackById_const(tid3)->mixIsReversed(audio2) == false);
+
+ // Create mix between cid2 and cid5
+ REQUIRE(timeline->mixClip(cid5));
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid2) == false);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid5) == true);
+ REQUIRE(timeline->getTrackById_const(tid3)->mixIsReversed(audio2) == false);
+ int audio5 = timeline->getClipSplitPartner(cid5);
+ REQUIRE(timeline->getTrackById_const(tid3)->mixIsReversed(audio5) == true);
+ // Undo cid5 mix
+ undoStack->undo();
+
+
+ state1b();
+ // Undo cid2 mix
+ undoStack->undo();
+ // Undo cid5
+ undoStack->undo();
+ undoStack->undo();
+ undoStack->undo();
+ undoStack->undo();
+
+ state0();
+ }
SECTION("Create mix on color clip and resize")
{
@@ -492,7 +573,7 @@
SECTION("Test chained mixes on color clips")
{
// Add 2 more color clips
- int cid5;
+ cid5 = -1;
int cid6;
int cid7;
state0();
@@ -567,6 +648,157 @@
state0();
}
+
+ SECTION("Test chained mixes and check mix direction")
+ {
+ // Add 2 more color clips
+ cid5 = -1;
+ int cid6;
+ int cid7;
+ state0();
+ REQUIRE(timeline->requestClipInsertion(binId2, tid2, 540, cid5));
+ REQUIRE(timeline->requestItemResize(cid5, 20, true, true));
+ REQUIRE(timeline->requestClipInsertion(binId2, tid2, 560, cid6));
+ REQUIRE(timeline->requestItemResize(cid6, 40, true, true));
+ REQUIRE(timeline->requestClipInsertion(binId2, tid2, 600, cid7));
+ REQUIRE(timeline->requestItemResize(cid7, 20, true, true));
+
+ // Cid3 pos=500, duration=20
+ // Cid4 pos=520, duration=20
+ // Cid5 pos=540, duration=20
+ // Cid6 pos=560, duration=40
+ // Cid7 pos=600, duration=20
+
+ auto mix0 = [&]() {
+ REQUIRE(timeline->getClipsCount() == 9);
+ REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 1);
+ REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 1);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid5) == false);
+ };
+
+ auto mix1 = [&]() {
+ REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 1);
+ REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 2);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid5) == false);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid6) == true);
+ };
+
+ auto mix2 = [&]() {
+ REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 1);
+ REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 1);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 3);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid5) == false);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid6) == true);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid7) == false);
+ };
+
+ auto mix3 = [&]() {
+ REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 1);
+ REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 1);
+ REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 0);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 4);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid4) == false);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid5) == true);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid6) == false);
+ REQUIRE(timeline->getTrackById_const(tid2)->mixIsReversed(cid7) == true);
+ };
+
+ // Mix 4 and 5
+ REQUIRE(timeline->mixClip(cid5));
+ mix0();
+
+ // Mix 5 and 6
+ REQUIRE(timeline->mixClip(cid6));
+ mix1();
+
+ // Mix 6 and 7
+ REQUIRE(timeline->mixClip(cid7));
+ mix2();
+
+ // Mix 3 and 4, this will revert all subsequent mixes
+ REQUIRE(timeline->mixClip(cid4));
+ mix3();
+
+ // Undo mix 3 and 4
+ undoStack->undo();
+ mix2();
+
+ // Now switch mixes to Slide type
+ timeline->switchComposition(cid7, QString("slide"));
+ timeline->switchComposition(cid6, QString("slide"));
+ timeline->switchComposition(cid5, QString("slide"));
+ mix2();
+
+ // Mix 3 and 4, this will revert all subsequent mixes
+ REQUIRE(timeline->mixClip(cid4));
+ mix3();
+
+ // Undo mix 3 and 4
+ undoStack->undo();
+ mix2();
+
+ // Now switch mixes to Wipe type
+ timeline->switchComposition(cid7, QString("wipe"));
+ timeline->switchComposition(cid6, QString("wipe"));
+ timeline->switchComposition(cid5, QString("wipe"));
+ mix2();
+
+ // Mix 3 and 4, this will revert all subsequent mixes
+ REQUIRE(timeline->mixClip(cid4));
+ mix3();
+
+ // Undo mix 3 and 4
+ undoStack->undo();
+ mix2();
+
+ // Undo Wipe mix switch on cid5
+ undoStack->undo();
+ // Undo mix switch on cid6
+ undoStack->undo();
+ // Undo mix switch on cid7
+ undoStack->undo();
+ mix2();
+
+ // Undo Slide mix switch on cid5
+ undoStack->undo();
+ // Undo mix switch on cid6
+ undoStack->undo();
+ // Undo mix switch on cid7
+ undoStack->undo();
+ mix2();
+
+ // Undo mix 6 and 7
+ undoStack->undo();
+ mix1();
+ // Undo mix 5 and 6
+ undoStack->undo();
+ mix0();
+ // Undo mix 4 and 5
+ undoStack->undo();
+
+ // Undo insert/resize ops
+ undoStack->undo();
+ undoStack->undo();
+ undoStack->undo();
+ undoStack->undo();
+ undoStack->undo();
+ undoStack->undo();
+
+ state0();
+ }
binModel->clean();
pCore->m_projectManager = nullptr;
}
diff -Nru kdenlive-22.04.1/tests/movetest.cpp kdenlive-22.04.2/tests/movetest.cpp
--- kdenlive-22.04.1/tests/movetest.cpp 1970-01-01 00:00:00.000000000 +0000
+++ kdenlive-22.04.2/tests/movetest.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -0,0 +1,99 @@
+#include "catch.hpp"
+#include "doc/docundostack.hpp"
+#include "test_utils.hpp"
+
+#include "definitions.h"
+#define private public
+#define protected public
+#include "core.h"
+
+using namespace fakeit;
+Mlt::Profile profile_move;
+
+TEST_CASE("Cut undo/redo", "[MoveClips]")
+{
+ // Create timeline
+ auto binModel = pCore->projectItemModel();
+ binModel->clean();
+ std::shared_ptr undoStack = std::make_shared(nullptr);
+ std::shared_ptr guideModel = std::make_shared(undoStack);
+
+ // Here we do some trickery to enable testing.
+ // We mock the project class so that the undoStack function returns our undoStack
+
+ Mock pmMock;
+ When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
+ When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
+
+ ProjectManager &mocked = pmMock.get();
+ pCore->m_projectManager = &mocked;
+
+ // We also mock timeline object to spy few functions and mock others
+ TimelineItemModel tim(&profile_move, undoStack);
+ Mock timMock(tim);
+ auto timeline = std::shared_ptr(&timMock.get(), [](...) {});
+ TimelineItemModel::finishConstruct(timeline, guideModel);
+
+ // Create a request
+ int tid1 = TrackModel::construct(timeline, -1, -1, QString(), true);
+ int tid3 = TrackModel::construct(timeline, -1, -1, QString(), true);
+ int tid2 = TrackModel::construct(timeline);
+ int tid4 = TrackModel::construct(timeline);
+
+ // Create clip with audio (40 frames long)
+ QString binId = createAVProducer(profile_move, binModel);
+
+ // Setup insert stream data
+ QMap audioInfo;
+ audioInfo.insert(1,QStringLiteral("stream1"));
+ timeline->m_binAudioTargets = audioInfo;
+
+ // Create AV clip 1
+ int cid1;
+ int cid2;
+ int cid3;
+ int cid4;
+
+ REQUIRE(timeline->requestClipInsertion(binId, tid2, 100, cid1));
+ cid2 = timeline->getClipSplitPartner(cid1);
+
+ SECTION("Ensure all clip instances on a track use the same producer")
+ {
+ REQUIRE(timeline->getItemTrackId(cid2) == tid3);
+ REQUIRE(timeline->getItemTrackId(cid1) == tid2);
+ Mlt::Producer prod1 = *(timeline->getClipPtr(cid1));
+ Mlt::Producer prod2 = *(timeline->getClipPtr(cid2));
+ // Clips on different tracks shoud not use the same producer
+ REQUIRE(!prod1.same_clip(prod2));
+
+ // Split clip
+ REQUIRE(TimelineFunctions::requestClipCut(timeline, cid1, 110));
+ cid3 = timeline->getClipByPosition(tid2, 111);
+ cid4 = timeline->getClipSplitPartner(cid3);
+ REQUIRE(timeline->getItemTrackId(cid4) == tid3);
+ REQUIRE(timeline->getItemTrackId(cid3) == tid2);
+
+ Mlt::Producer prod3 = *(timeline->getClipPtr(cid3));
+ Mlt::Producer prod4 = *(timeline->getClipPtr(cid4));
+ // Clips on different tracks shoud not use the same producer
+ REQUIRE(!prod3.same_clip(prod4));
+ // Clips on same track shoud use the same producer
+ REQUIRE(prod1.same_clip(prod3));
+ REQUIRE(prod2.same_clip(prod4));
+
+ // Undo and redo cut, then ensure the producers are still correct
+ undoStack->undo();
+ undoStack->redo();
+
+ prod3 = *(timeline->getClipPtr(cid3));
+ prod4 = *(timeline->getClipPtr(cid4));
+ // Clips on different tracks shoud not use the same producer
+ REQUIRE(!prod3.same_clip(prod4));
+ // Clips on same track shoud use the same producer
+ REQUIRE(prod1.same_clip(prod3));
+ REQUIRE(prod2.same_clip(prod4));
+ }
+ binModel->clean();
+ pCore->m_projectManager = nullptr;
+
+}
diff -Nru kdenlive-22.04.1/tests/snaptest.cpp kdenlive-22.04.2/tests/snaptest.cpp
--- kdenlive-22.04.1/tests/snaptest.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/tests/snaptest.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -1,7 +1,5 @@
-#include "catch.hpp"
+#include "test_utils.hpp"
#include "timeline2/model/snapmodel.hpp"
-#include
-#include
TEST_CASE("Snap points model test", "[SnapModel]")
{
diff -Nru kdenlive-22.04.1/tests/test_utils.cpp kdenlive-22.04.2/tests/test_utils.cpp
--- kdenlive-22.04.1/tests/test_utils.cpp 2022-05-15 21:39:54.000000000 +0000
+++ kdenlive-22.04.2/tests/test_utils.cpp 2022-06-12 20:11:07.000000000 +0000
@@ -27,12 +27,12 @@
// In case the test system does not have avformat support, we can switch to the integrated blipflash producer
std::shared_ptr producer = std::make_shared(prof, "blipflash");
+ REQUIRE(producer->is_valid());
+
producer->set("length", length);
producer->set_in_and_out(0, length - 1);
producer->set("kdenlive:duration", length);
- REQUIRE(producer->is_valid());
-
QString binId = QString::number(binModel->getFreeClipId());
auto binClip = ProjectClip::construct(binId, QIcon(), binModel, producer);
binClip->forceLimitedDuration();