Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package shotcut for openSUSE:Factory checked 
in at 2021-09-20 23:32:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/shotcut (Old)
 and      /work/SRC/openSUSE:Factory/.shotcut.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "shotcut"

Mon Sep 20 23:32:49 2021 rev:47 rq:920032 version:21.09.13

Changes:
--------
--- /work/SRC/openSUSE:Factory/shotcut/shotcut.changes  2021-09-01 
21:37:51.520925823 +0200
+++ /work/SRC/openSUSE:Factory/.shotcut.new.1899/shotcut.changes        
2021-09-20 23:34:22.115263668 +0200
@@ -1,0 +2,15 @@
+Fri Sep 17 12:32:10 UTC 2021 - Michael Vetter <mvet...@suse.com>
+
+- Update to 21.09.13:
+  * Fixed dragging to Timeline broken if there is MLT XML on theclipboard 
(broke v21.08.29).
+  * Fixed seeking backwards after seeking to the end (broke in v21.08.29).
+  * Fixed File > Save (Ctrl-S) immediately after File > Close (Ctrl-W) can 
corrupt a saved project.
+  * Fixed Add Custom Video Mode > Frames/sec was not accepting some legitimate 
values.
+  * Fixed encoding HEVC without hardware encoder (x265) would always output 
10-bit instead of 8 (broke in v20.11.28).
+  * Changed Copy the filters to only copy enabled filters.
+  * Changed fading on the Timeline to automatically fade the opacity when it 
is not the bottom video track.
+  * Expire old QML cache items at startup to reduce app data footprint.
+  * Improved performance of 360: video filters.
+  * Added 360: Equirectangular to Stereographic video filter. This can be used 
to create so-called Tiny Planet videos.
+
+-------------------------------------------------------------------

Old:
----
  shotcut-21.08.29.tar.gz

New:
----
  shotcut-21.09.13.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ shotcut.spec ++++++
--- /var/tmp/diff_new_pack.v9chNr/_old  2021-09-20 23:34:22.707264399 +0200
+++ /var/tmp/diff_new_pack.v9chNr/_new  2021-09-20 23:34:22.707264399 +0200
@@ -25,7 +25,7 @@
 %global __requires_exclude 
qmlimport\\((Shotcut\\.Controls|org\\.shotcut\\.qml).*
 
 Name:           shotcut
-Version:        21.08.29
+Version:        21.09.13
 Release:        0
 # This package creates a build time version from the current date and uses it 
to check
 # for updates. See patch1 and prep/build section. For reproducible builds.

++++++ shotcut-21.08.29.tar.gz -> shotcut-21.09.13.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/scripts/build-shotcut-msys2.sh 
new/shotcut-21.09.13/scripts/build-shotcut-msys2.sh
--- old/shotcut-21.08.29/scripts/build-shotcut-msys2.sh 2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/scripts/build-shotcut-msys2.sh 2021-09-13 
07:05:06.000000000 +0200
@@ -42,13 +42,13 @@
 SHOTCUT_REVISION=
 SHOTCUT_VERSION=$(date '+%y.%m.%d')
 ENABLE_BIGSH0T=1
-BIGSH0T_HEAD=0
-BIGSH0T_REVISION="5fad6d3b5963ce69141e9debcc3b733b84a0842d"
+BIGSH0T_HEAD=1
+BIGSH0T_REVISION=
 ENABLE_ZIMG=1
 ZIMG_HEAD=1
 ZIMG_REVISION=
-DAV1D_HEAD=1
-DAV1D_REVISION=
+DAV1D_HEAD=0
+DAV1D_REVISION="0.9.2"
 AOM_HEAD=0
 AOM_REVISION="v2.0.2"
 ENABLE_VMAF=1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/scripts/build-shotcut.sh 
new/shotcut-21.09.13/scripts/build-shotcut.sh
--- old/shotcut-21.08.29/scripts/build-shotcut.sh       2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/scripts/build-shotcut.sh       2021-09-13 
07:05:06.000000000 +0200
@@ -73,13 +73,13 @@
 RUBBERBAND_HEAD=0
 RUBBERBAND_REVISION="v1.9.1"
 ENABLE_BIGSH0T=1
-BIGSH0T_HEAD=0
-BIGSH0T_REVISION="5fad6d3b5963ce69141e9debcc3b733b84a0842d"
+BIGSH0T_HEAD=1
+BIGSH0T_REVISION=
 ENABLE_ZIMG=1
 ZIMG_HEAD=1
 ZIMG_REVISION=
-DAV1D_HEAD=1
-DAV1D_REVISION=
+DAV1D_HEAD=0
+DAV1D_REVISION="0.9.2"
 AOM_HEAD=0
 AOM_REVISION="v2.0.2"
 VMAF_HEAD=0
@@ -858,11 +858,7 @@
   ######
   # x265
   CFLAGS_[13]=$CFLAGS
-  if test "$TARGET_OS" = "Win32" -o "$TARGET_OS" = "Win64" ; then
-    CONFIG[13]="cmake -DCMAKE_INSTALL_PREFIX=$FINAL_INSTALL_DIR 
-DCMAKE_TOOLCHAIN_FILE=my.cmake -DENABLE_CLI=OFF -DHIGH_BIT_DEPTH=ON 
$CMAKE_DEBUG_FLAG"
-  else
-    CONFIG[13]="cmake -DCMAKE_INSTALL_PREFIX=$FINAL_INSTALL_DIR 
-DENABLE_CLI=OFF -DHIGH_BIT_DEPTH=ON $CMAKE_DEBUG_FLAG"
-  fi
+  CONFIG[13]="cmake -G Ninja -D CMAKE_INSTALL_PREFIX=$FINAL_INSTALL_DIR 
-DENABLE_CLI=OFF -D ENABLE_SHARED=ON -D EXTRA_LIB='x265_main10.a' -D 
LINKED_10BIT=ON -D EXTRA_LINK_FLAGS='-L.' $CMAKE_DEBUG_FLAG"
   LDFLAGS_[13]=$LDFLAGS
 
   #######
@@ -1482,7 +1478,12 @@
 
   # Special hack for x265
   if test "x265" = "$1"; then
-    cd source
+    [ ! -d "10bit" ] && mkdir 10bit
+    cd 10bit
+    cmd cmake -G Ninja -D ENABLE_CLI=OFF -D ENABLE_SHARED=OFF -D 
EXPORT_C_API=OFF -D HIGH_BIT_DEPTH=ON $CMAKE_DEBUG_FLAG ../source
+    cmd ninja
+    cd ../source
+    cmd ln -s ../10bit/libx265.a libx265_main10.a
   fi
 
   # Special hack for AMF
@@ -1544,6 +1545,16 @@
     cmd ninja -C builddir -j $MAKEJ || die "Unable to build $1"
   elif test "aom" = "$1" -o "mlt" = "$1"; then
     cmd ninja -j $MAKEJ || die "Unable to build $1"
+  elif test "x265" = "$1" ; then
+    cmd ninja -j $MAKEJ || die "Unable to build $1"
+    cmd mv libx265.a libx265_main.a
+    ar -M <<EOF
+CREATE libx265.a
+ADDLIB libx265_main.a
+ADDLIB libx265_main10.a
+SAVE
+END
+EOF
   elif test "vmaf" = "$1" ; then
     cmd ninja -C libvmaf/build -j $MAKEJ || die "Unable to build $1"
   elif test "$MYCONFIG" != ""; then
@@ -1593,7 +1604,7 @@
       fi
     elif test "dav1d" = "$1" -o "rubberband" = "$1" ; then
       cmd meson install -C builddir || die "Unable to install $1"
-    elif test "aom" = "$1" -o "mlt" = "$1" ; then
+    elif test "aom" = "$1" -o "mlt" = "$1" -o "x265" = "$1" ; then
       cmd ninja install || die "Unable to install $1"
     elif test "vmaf" = "$1" ; then
       cmd ninja install -C libvmaf/build || die "Unable to install $1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/dialogs/customprofiledialog.cpp 
new/shotcut-21.09.13/src/dialogs/customprofiledialog.cpp
--- old/shotcut-21.08.29/src/dialogs/customprofiledialog.cpp    2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/dialogs/customprofiledialog.cpp    2021-09-13 
07:05:06.000000000 +0200
@@ -57,23 +57,9 @@
     QSize sar(ui->aspectNumSpinner->value() * ui->heightSpinner->value(), 
ui->aspectDenSpinner->value() * ui->widthSpinner->value());
     auto gcd = Util::greatestCommonDivisor(sar.width(), sar.height());
     MLT.profile().set_sample_aspect(sar.width() / gcd, sar.height() / gcd);
-    switch (int(ui->fpsSpinner->value() * 10)) {
-    case 239:
-        MLT.profile().set_frame_rate(24000, 1001);
-        break;
-    case 299:
-        MLT.profile().set_frame_rate(30000, 1001);
-        break;
-    case 479:
-        MLT.profile().set_frame_rate(48000, 1001);
-        break;
-    case 599:
-        MLT.profile().set_frame_rate(60000, 1001);
-        break;
-    default:
-        MLT.profile().set_frame_rate(ui->fpsSpinner->value() * 1000, 1000);
-        break;
-    }
+    int numerator, denominator;
+    Util::normalizeFrameRate(ui->fpsSpinner->value(), numerator, denominator);
+    MLT.profile().set_frame_rate(numerator, denominator);
     MLT.profile().set_progressive(ui->scanModeCombo->currentIndex());
     MLT.profile().set_colorspace((ui->colorspaceCombo->currentIndex() == 1)? 
709 : 601);
     MLT.updatePreviewProfile();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/docks/encodedock.cpp 
new/shotcut-21.09.13/src/docks/encodedock.cpp
--- old/shotcut-21.08.29/src/docks/encodedock.cpp       2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/docks/encodedock.cpp       2021-09-13 
07:05:06.000000000 +0200
@@ -845,24 +845,10 @@
                 // Only if the frame rate spinner does not match the profile.
                 && (includeProfile || qRound(ui->fpsSpinner->value() * 
1000000.0) != qRound(MLT.profile().fps() * 1000000.0)))
             {
-                // Convert some common non-integer frame rates to fractions.
-                if (qRound(ui->fpsSpinner->value() * 1000000.0) == 23976024) {
-                    p->set("frame_rate_num", 24000);
-                    p->set("frame_rate_den", 1001);
-                } else if (qRound(ui->fpsSpinner->value() * 100000.0) == 
2997003) {
-                    p->set("frame_rate_num", 30000);
-                    p->set("frame_rate_den", 1001);
-                } else if (qRound(ui->fpsSpinner->value() * 1000000.0) == 
47952048) {
-                    p->set("frame_rate_num", 48000);
-                    p->set("frame_rate_den", 1001);
-                } else if (qRound(ui->fpsSpinner->value() * 100000.0) == 
5994006) {
-                    p->set("frame_rate_num", 60000);
-                    p->set("frame_rate_den", 1001);
-                } else {
-                    // Workaround storing QDoubleSpinBox::value() loses 
precision.
-                    p->set("frame_rate_num", qRound(ui->fpsSpinner->value() * 
1000000.0));
-                    p->set("frame_rate_den", 1000000);
-                }
+                int numerator, denominator;
+                Util::normalizeFrameRate(ui->fpsSpinner->value(), numerator, 
denominator);
+                p->set("frame_rate_num", numerator);
+                p->set("frame_rate_den", denominator);
             }
             if (ui->formatCombo->currentText() == "image2")
                 setIfNotSet(p, "threads", 1);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/docks/timelinedock.cpp 
new/shotcut-21.09.13/src/docks/timelinedock.cpp
--- old/shotcut-21.08.29/src/docks/timelinedock.cpp     2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/docks/timelinedock.cpp     2021-09-13 
07:05:06.000000000 +0200
@@ -1233,7 +1233,7 @@
             Mlt::Producer producer(MLT.isClip()? MLT.producer() : 
MLT.savedProducer());
             ProxyManager::generateIfNotExists(producer);
             xmlToUse = MLT.XML(&producer);
-        } else if (xmlToUse.isEmpty()) {
+        } else if (!xml.isEmpty()) {
             xmlToUse = convertUrlsToXML(xml);
             if (xml.startsWith(kFileUrlProtocol) && 
xml.split(kFilesUrlDelimiter).size() > 1) {
                 selectBlocker.reset(new TimelineSelectionBlocker(*this));
@@ -1313,7 +1313,7 @@
             Mlt::Producer producer(MLT.isClip()? MLT.producer() : 
MLT.savedProducer());
             ProxyManager::generateIfNotExists(producer);
             xmlToUse = MLT.XML(&producer);
-        } else if (xmlToUse.isEmpty()) {
+        } else if (!xml.isEmpty()) {
             xmlToUse = convertUrlsToXML(xml);
             if (xml.startsWith(kFileUrlProtocol) && 
xml.split(kFilesUrlDelimiter).size() > 1) {
                 selectBlocker.reset(new TimelineSelectionBlocker(*this));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/main.cpp 
new/shotcut-21.09.13/src/main.cpp
--- old/shotcut-21.08.29/src/main.cpp   2021-08-29 07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/main.cpp   2021-09-13 07:05:06.000000000 +0200
@@ -45,6 +45,7 @@
 }
 #endif
 
+static const int kMaxCacheCount = 5000;
 #ifdef Q_OS_WIN
 static const char* kDefaultScaleRoundPolicy = "RoundPreferFloor";
 #else
@@ -370,6 +371,26 @@
     splash.show();
     a.processEvents();
 
+    // Expire old items from the qmlcache
+    splash.showMessage(QCoreApplication::translate("main", "Expiring 
cache..."), Qt::AlignRight | Qt::AlignVCenter);
+    a.processEvents();
+    auto dir = 
QDir(QStandardPaths::standardLocations(QStandardPaths::CacheLocation).constFirst());
+    if (dir.exists() && dir.cd("qmlcache")) {
+        auto ls = dir.entryList(QDir::Files | QDir::Readable | 
QDir::NoDotAndDotDot, QDir::Time);
+        if (qMax(0, ls.size() - kMaxCacheCount) > 0) {
+            LOG_INFO() << "removing" << qMax(0, ls.size() - kMaxCacheCount) << 
"from" << dir.path();
+        }
+        for (int i = kMaxCacheCount; i < ls.size(); i++) {
+            QString filePath = dir.filePath(ls[i]);
+            if (!QFile::remove(filePath)) {
+                LOG_WARNING() << "failed to delete" << filePath;
+            }
+            if (i % 1000 == 0) {
+                a.processEvents();
+            }
+        }
+    }
+
     QQuickStyle::setStyle("Fusion");
     a.setProperty("system-style", a.style()->objectName());
     MainWindow::changeTheme(Settings.theme());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/mainwindow.cpp 
new/shotcut-21.09.13/src/mainwindow.cpp
--- old/shotcut-21.08.29/src/mainwindow.cpp     2021-08-29 07:29:22.000000000 
+0200
+++ new/shotcut-21.09.13/src/mainwindow.cpp     2021-09-13 07:05:06.000000000 
+0200
@@ -188,6 +188,7 @@
     connect(ui->actionAbout_Qt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
     connect(this, &MainWindow::producerOpened, this, 
&MainWindow::onProducerOpened);
     connect(ui->mainToolBar, SIGNAL(visibilityChanged(bool)), 
SLOT(onToolbarVisibilityChanged(bool)));
+    ui->actionSave->setEnabled(false);
 
     // Accept drag-n-drop of files.
     this->setAcceptDrops(true);
@@ -2687,6 +2688,7 @@
         Util::getHash(*MLT.producer());
         ui->actionPaste->setEnabled(true);
     }
+    ui->actionSave->setEnabled(true);
     QMutexLocker locker(&m_autosaveMutex);
     if (m_autosaveFile)
         setCurrentFile(m_autosaveFile->managedFileName());
@@ -3960,7 +3962,13 @@
 {
     if (continueModified()) {
         LOG_DEBUG() << "";
+        QMutexLocker locker(&m_autosaveMutex);
+        m_autosaveFile.reset();
+        locker.unlock();
+        setCurrentFile("");
+        MLT.resetURL();
         MLT.setProjectFolder(QString());
+        ui->actionSave->setEnabled(false);
         MLT.stop();
         if (multitrack())
             m_timelineDock->model()->close();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/mltcontroller.cpp 
new/shotcut-21.09.13/src/mltcontroller.cpp
--- old/shotcut-21.08.29/src/mltcontroller.cpp  2021-08-29 07:29:22.000000000 
+0200
+++ new/shotcut-21.09.13/src/mltcontroller.cpp  2021-09-13 07:05:06.000000000 
+0200
@@ -970,7 +970,7 @@
     return newUid;
 }
 
-void Controller::copyFilters(Producer& fromProducer, Producer& toProducer, 
bool fromClipboard)
+void Controller::copyFilters(Producer& fromProducer, Producer& toProducer, 
bool fromClipboard, bool includeDisabled)
 {
     int in = fromProducer.get(kFilterInProperty)? 
fromProducer.get_int(kFilterInProperty) : fromProducer.get_in();
     int out = fromProducer.get(kFilterOutProperty)? 
fromProducer.get_int(kFilterOutProperty) : fromProducer.get_out();
@@ -978,7 +978,8 @@
     
     for (int i = 0; i < count; i++) {
         QScopedPointer<Mlt::Filter> fromFilter(fromProducer.filter(i));
-        if (fromFilter && fromFilter->is_valid() && 
!fromFilter->get_int("_loader") && fromFilter->get("mlt_service")) {
+        if (fromFilter && fromFilter->is_valid() && 
!fromFilter->get_int("_loader") && fromFilter->get("mlt_service")
+            && (includeDisabled || !fromFilter->get_int("disable"))) {
             Mlt::Filter toFilter(MLT.profile(), 
fromFilter->get("mlt_service"));
             if (toFilter.is_valid()) {
                 toFilter.inherit(*fromFilter);
@@ -1015,10 +1016,10 @@
 {
     if (producer && producer->is_valid()) {
         initFiltersClipboard();
-        copyFilters(*producer, *m_filtersClipboard);
+        copyFilters(*producer, *m_filtersClipboard, false, false);
     } else if (m_producer && m_producer->is_valid()) {
         initFiltersClipboard();
-        copyFilters(*m_producer, *m_filtersClipboard);
+        copyFilters(*m_producer, *m_filtersClipboard, false, false);
     }
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/mltcontroller.h 
new/shotcut-21.09.13/src/mltcontroller.h
--- old/shotcut-21.08.29/src/mltcontroller.h    2021-08-29 07:29:22.000000000 
+0200
+++ new/shotcut-21.09.13/src/mltcontroller.h    2021-09-13 07:05:06.000000000 
+0200
@@ -123,7 +123,7 @@
     QUuid uuid(Mlt::Properties &properties) const;
     void setUuid(Mlt::Properties &properties, QUuid uid) const;
     QUuid ensureHasUuid(Mlt::Properties& properties) const;
-    static void copyFilters(Mlt::Producer& fromProducer, Mlt::Producer& 
toProducer, bool fromClipboard = false);
+    static void copyFilters(Mlt::Producer& fromProducer, Mlt::Producer& 
toProducer, bool fromClipboard = false, bool includeDisabled = true);
     void copyFilters(Mlt::Producer* producer = nullptr);
     void pasteFilters(Mlt::Producer* producer = nullptr, Mlt::Producer* 
fromProducer = nullptr);
     static void adjustFilters(Mlt::Producer& producer, int startIndex = 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/models/multitrackmodel.cpp 
new/shotcut-21.09.13/src/models/multitrackmodel.cpp
--- old/shotcut-21.08.29/src/models/multitrackmodel.cpp 2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/models/multitrackmodel.cpp 2021-09-13 
07:05:06.000000000 +0200
@@ -1365,13 +1365,18 @@
                         if (Settings.playerGPU()) {
                             Mlt::Filter f(MLT.profile(), "movit.opacity");
                             f.set(kShotcutFilterProperty, "fadeInMovit");
-                            f.set("alpha", 1);
+                            f.set("alpha", i == bottomVideoTrackMltIndex()? 1 
: -1);
                             info->producer->attach(f);
                             filter.reset(new Mlt::Filter(f));
                         } else {
                             Mlt::Filter f(MLT.profile(), "brightness");
                             f.set(kShotcutFilterProperty, "fadeInBrightness");
-                            f.set("alpha", 1);
+                            if (i == bottomVideoTrackMltIndex()) {
+                                f.set("alpha", 1);
+                            } else {
+                                f.set("alpha", 0);
+                                f.set("level", 1);
+                            }
                             info->producer->attach(f);
                             filter.reset(new Mlt::Filter(f));
                         }
@@ -1473,13 +1478,18 @@
                         if (Settings.playerGPU()) {
                             Mlt::Filter f(MLT.profile(), "movit.opacity");
                             f.set(kShotcutFilterProperty, "fadeOutMovit");
-                            f.set("alpha", 1);
+                            f.set("alpha", i == bottomVideoTrackMltIndex()? 1 
: -1);
                             info->producer->attach(f);
                             filter.reset(new Mlt::Filter(f));
                         } else {
                             Mlt::Filter f(MLT.profile(), "brightness");
                             f.set(kShotcutFilterProperty, "fadeOutBrightness");
-                            f.set("alpha", 1);
+                            if (i == bottomVideoTrackMltIndex()) {
+                                f.set("alpha", 1);
+                            } else {
+                                f.set("alpha", 0);
+                                f.set("level", 1);
+                            }
                             info->producer->attach(f);
                             filter.reset(new Mlt::Filter(f));
                         }
@@ -2455,14 +2465,20 @@
     return transition;
 }
 
-void MultitrackModel::refreshVideoBlendTransitions()
+int MultitrackModel::bottomVideoTrackMltIndex() const
 {
     // Get the MLT index of the bottom-most video track
-    int a_track = 0;
+    int track = 0;
     for (auto& t : m_trackList) {
         if (t.type == VideoTrackType)
-            a_track = t.mlt_index;
+            track = t.mlt_index;
     }
+    return track;
+}
+
+void MultitrackModel::refreshVideoBlendTransitions()
+{
+    int a_track = bottomVideoTrackMltIndex();
     // For each video track
     for (auto& t : m_trackList) {
         if (t.type == VideoTrackType) {
@@ -3319,11 +3335,7 @@
     }
 
     // Change a_track of composite transitions to bottom video track.
-    int a_track = 0;
-    foreach (Track t, m_trackList) {
-        if (t.type == VideoTrackType)
-            a_track = t.mlt_index;
-    }
+    int a_track = bottomVideoTrackMltIndex();
     foreach (Track t, m_trackList) {
         if (t.type == VideoTrackType) {
             QScopedPointer<Mlt::Transition> 
transition(getVideoBlendTransition(t.mlt_index));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/models/multitrackmodel.h 
new/shotcut-21.09.13/src/models/multitrackmodel.h
--- old/shotcut-21.08.29/src/models/multitrackmodel.h   2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/models/multitrackmodel.h   2021-09-13 
07:05:06.000000000 +0200
@@ -194,6 +194,7 @@
     bool warnIfInvalid(Mlt::Service& service);
     Mlt::Transition* getVideoBlendTransition(int trackIndex) const;
     void refreshVideoBlendTransitions();
+    int bottomVideoTrackMltIndex() const;
 
     friend class UndoHelper;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/player.cpp 
new/shotcut-21.09.13/src/player.cpp
--- old/shotcut-21.08.29/src/player.cpp 2021-08-29 07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/player.cpp 2021-09-13 07:05:06.000000000 +0200
@@ -690,7 +690,7 @@
         onProducerOpened(false);
     }
     int position = frame.get_position();
-    if (position < m_duration) {
+    if (position <= m_duration) {
         m_position = position;
         m_positionSpinner->blockSignals(true);
         m_positionSpinner->setValue(position);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml 
new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml
--- old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml  
1970-01-01 01:00:00.000000000 +0100
+++ new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml  
2021-09-13 07:05:06.000000000 +0200
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+import QtQuick 2.0
+import org.shotcut.qml 1.0
+
+Metadata {
+    type: Metadata.Filter
+    name: qsTr("360: Equirectangular to Stereographic")
+    mlt_service: "frei0r.bigsh0t_eq_to_stereo"
+    objectName: "bigsh0t_eq_to_stereo"
+    qml: "ui.qml"
+    vui: "vui.qml"
+    keyframes {
+        allowAnimateIn: true
+        allowAnimateOut: true
+        simpleProperties: ['yaw', "pitch", "roll", "fov", "amount"]
+        parameters: [
+            Parameter {
+                name: qsTr('Yaw')
+                property: 'yaw'
+                isSimple: true
+                isCurve: true
+                minimum: -360
+                maximum: 360
+            },
+            Parameter {
+                name: qsTr('Pitch')
+                property: 'pitch'
+                isSimple: true
+                isCurve: true
+                minimum: -180
+                maximum: 180
+            },
+            Parameter {
+                name: qsTr('Roll')
+                property: 'roll'
+                isSimple: true
+                isCurve: true
+                minimum: -180
+                maximum: 180
+            },
+            Parameter {
+                name: qsTr('FOV')
+                property: 'fov'
+                isSimple: true
+                isCurve: true
+                minimum: 0
+                maximum: 180
+            },
+                       Parameter {
+                name: qsTr('Amount')
+                property: 'amount'
+                isSimple: true
+                isCurve: true
+                minimum: 0
+                maximum: 100
+            }
+        ]
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml 
new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml
--- old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml    
1970-01-01 01:00:00.000000000 +0100
+++ new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml    
2021-09-13 07:05:06.000000000 +0200
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import Shotcut.Controls 1.0 as Shotcut
+
+
+Item {
+    width: 350
+    height: 200
+    property bool blockUpdate: true
+
+    property double yawStart : 0.0; property double yawMiddle : 0.0; property 
double yawEnd : 0.0;
+    property double pitchStart : 0.0; property double pitchMiddle : 0.0; 
property double pitchEnd : 0.0;
+    property double rollStart : 0.0; property double rollMiddle : 0.0; 
property double rollEnd : 0.0;
+    property double fovStart : 0.0; property double fovMiddle : 0.0; property 
double fovEnd : 0.0;
+    property double amountStart : 0.0; property double amountMiddle : 0.0; 
property double amountEnd : 0.0;
+    property int interpolationValue : 0;
+
+    function updateSimpleKeyframes() {
+        if (filter.animateIn <= 0 && filter.animateOut <= 0) {
+            // When disabling simple keyframes. Clear out the keyframes before 
proceeding.
+            filter.blockSignals = true
+            if (!yawKeyframesButton.checked && filter.keyframeCount("yaw") > 
0) {
+                filter.resetProperty("yaw")
+            }
+            if (!pitchKeyframesButton.checked && filter.keyframeCount("pitch") 
> 0) {
+                filter.resetProperty("pitch")
+            }
+            if (!rollKeyframesButton.checked && filter.keyframeCount("roll") > 
0) {
+                filter.resetProperty("roll")
+            }
+            if (!fovKeyframesButton.checked && filter.keyframeCount("fov") > 
0) {
+                filter.resetProperty("fov")
+            }
+            if (!amountKeyframesButton.checked && 
filter.keyframeCount("amount") > 0) {
+                filter.resetProperty("amount")
+            }
+            filter.blockSignals = false
+        } else if (filter.animateIn > 0 || filter.animateOut > 0) {
+            // When enabling simple keyframes, initialize the keyframes with 
the current value
+            if (filter.keyframeCount("yaw") <= 0) {
+                yawStart = yawMiddle = yawEnd = filter.getDouble("yaw")
+            }
+            if (filter.keyframeCount("pitch") <= 0) {
+                pitchStart = pitchMiddle = pitchEnd = filter.getDouble("pitch")
+            }
+            if (filter.keyframeCount("roll") <= 0) {
+                rollStart = rollMiddle = rollEnd = filter.getDouble("roll")
+            }
+            if (filter.keyframeCount("fov") <= 0) {
+                fovStart = fovMiddle = fovEnd = filter.getDouble("fov")
+            }
+            if (filter.keyframeCount("amount") <= 0) {
+                amountStart = amountMiddle = amountEnd = 
filter.getDouble("amount")
+            }
+        }
+        updateProperty_yaw (null)
+        updateProperty_pitch (null)
+        updateProperty_roll (null)
+        updateProperty_fov (null)
+        updateProperty_amount (null)
+        updateProperty_interpolation ()
+    }
+
+    Connections {
+        target: filter
+        onChanged: setControls()
+        onInChanged: updateSimpleKeyframes()
+        onOutChanged: updateSimpleKeyframes()
+        onAnimateInChanged: updateSimpleKeyframes()
+        onAnimateOutChanged: updateSimpleKeyframes()
+    }
+
+    Component.onCompleted: {
+        if (filter.isNew) { filter.set("yaw", 0); } else { yawMiddle = 
filter.getDouble("yaw", filter.animateIn); if (filter.animateIn > 0) { yawStart 
= filter.getDouble("yaw", 0); } if (filter.animateOut > 0) { yawEnd = 
filter.getDouble("yaw", filter.duration - 1); } }
+        if (filter.isNew) { filter.set("pitch", -90); } else { pitchMiddle = 
filter.getDouble("pitch", filter.animateIn); if (filter.animateIn > 0) { 
pitchStart = filter.getDouble("pitch", 0); } if (filter.animateOut > 0) { 
pitchEnd = filter.getDouble("pitch", filter.duration - 1); } }
+        if (filter.isNew) { filter.set("roll", 0); } else { rollMiddle = 
filter.getDouble("roll", filter.animateIn); if (filter.animateIn > 0) { 
rollStart = filter.getDouble("roll", 0); } if (filter.animateOut > 0) { rollEnd 
= filter.getDouble("roll", filter.duration - 1); } }
+        if (filter.isNew) { filter.set("fov", 160); } else { fovMiddle = 
filter.getDouble("fov", filter.animateIn); if (filter.animateIn > 0) { fovStart 
= filter.getDouble("fov", 0); } if (filter.animateOut > 0) { fovEnd = 
filter.getDouble("fov", filter.duration - 1); } }
+        if (filter.isNew) { filter.set("amount", 100); } else { amountMiddle = 
filter.getDouble("amount", filter.animateIn); if (filter.animateIn > 0) { 
amountStart = filter.getDouble("amount", 0); } if (filter.animateOut > 0) { 
amountEnd = filter.getDouble("amount", filter.duration - 1); } }
+        if (filter.isNew) { filter.set("interpolation", 1); } else { 
interpolationValue = filter.get("interpolation"); }
+
+        if (filter.isNew) {
+            filter.savePreset(preset.parameters)
+        }
+        setControls()
+    }
+
+    function setControls() {
+        var position = getPosition()
+        blockUpdate = true
+        yawSlider.value = filter.getDouble("yaw", position)
+        yawKeyframesButton.checked = filter.animateIn <= 0 && 
filter.animateOut <= 0 && filter.keyframeCount("yaw") > 0
+        pitchSlider.value = filter.getDouble("pitch", position)
+        pitchKeyframesButton.checked = filter.animateIn <= 0 && 
filter.animateOut <= 0 && filter.keyframeCount("pitch") > 0
+        rollSlider.value = filter.getDouble("roll", position)
+        rollKeyframesButton.checked = filter.animateIn <= 0 && 
filter.animateOut <= 0 && filter.keyframeCount("roll") > 0
+        fovSlider.value = filter.getDouble("fov", position)
+        fovKeyframesButton.checked = filter.animateIn <= 0 && 
filter.animateOut <= 0 && filter.keyframeCount("fov") > 0
+        amountSlider.value = filter.getDouble("amount", position)
+        interpolationComboBox.currentIndex = filter.get("interpolation")
+        blockUpdate = false
+    }
+
+    function updateProperty_yaw (position) { if (blockUpdate) return; var 
value = yawSlider.value; if (position !== null) { if (position <= 0 && 
filter.animateIn > 0) { yawStart = value; } else if (position >= 
filter.duration - 1 && filter.animateOut > 0) { yawEnd = value; } else { 
yawMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("yaw"); yawKeyframesButton.checked = false; if 
(filter.animateIn > 0) { filter.set("yaw", yawStart, 0); filter.set("yaw", 
yawMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { 
filter.set("yaw", yawMiddle, filter.duration - filter.animateOut); 
filter.set("yaw", yawEnd, filter.duration - 1); } } else if 
(!yawKeyframesButton.checked) { filter.resetProperty("yaw"); filter.set("yaw", 
yawMiddle); } else if (position !== null) { filter.set("yaw", value, position); 
} }
+    function updateProperty_pitch (position) { if (blockUpdate) return; var 
value = pitchSlider.value; if (position !== null) { if (position <= 0 && 
filter.animateIn > 0) { pitchStart = value; } else if (position >= 
filter.duration - 1 && filter.animateOut > 0) { pitchEnd = value; } else { 
pitchMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("pitch"); pitchKeyframesButton.checked = false; if 
(filter.animateIn > 0) { filter.set("pitch", pitchStart, 0); 
filter.set("pitch", pitchMiddle, filter.animateIn - 1); } if (filter.animateOut 
> 0) { filter.set("pitch", pitchMiddle, filter.duration - filter.animateOut); 
filter.set("pitch", pitchEnd, filter.duration - 1); } } else if 
(!pitchKeyframesButton.checked) { filter.resetProperty("pitch"); 
filter.set("pitch", pitchMiddle); } else if (position !== null) { 
filter.set("pitch", value, position); } }
+    function updateProperty_roll (position) { if (blockUpdate) return; var 
value = rollSlider.value; if (position !== null) { if (position <= 0 && 
filter.animateIn > 0) { rollStart = value; } else if (position >= 
filter.duration - 1 && filter.animateOut > 0) { rollEnd = value; } else { 
rollMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("roll"); rollKeyframesButton.checked = false; if 
(filter.animateIn > 0) { filter.set("roll", rollStart, 0); filter.set("roll", 
rollMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { 
filter.set("roll", rollMiddle, filter.duration - filter.animateOut); 
filter.set("roll", rollEnd, filter.duration - 1); } } else if 
(!rollKeyframesButton.checked) { filter.resetProperty("roll"); 
filter.set("roll", rollMiddle); } else if (position !== null) { 
filter.set("roll", value, position); } }
+    function updateProperty_fov (position) { if (blockUpdate) return; var 
value = fovSlider.value; if (position !== null) { if (position <= 0 && 
filter.animateIn > 0) { fovStart = value; } else if (position >= 
filter.duration - 1 && filter.animateOut > 0) { fovEnd = value; } else { 
fovMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("fov"); fovKeyframesButton.checked = false; if 
(filter.animateIn > 0) { filter.set("fov", fovStart, 0); filter.set("fov", 
fovMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { 
filter.set("fov", fovMiddle, filter.duration - filter.animateOut); 
filter.set("fov", fovEnd, filter.duration - 1); } } else if 
(!fovKeyframesButton.checked) { filter.resetProperty("fov"); filter.set("fov", 
fovMiddle); } else if (position !== null) { filter.set("fov", value, position); 
} }
+    function updateProperty_amount (position) { if (blockUpdate) return; var 
value = amountSlider.value; if (position !== null) { if (position <= 0 && 
filter.animateIn > 0) { amountStart = value; } else if (position >= 
filter.duration - 1 && filter.animateOut > 0) { amountEnd = value; } else { 
amountMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("amount"); amountKeyframesButton.checked = false; if 
(filter.animateIn > 0) { filter.set("amount", amountStart, 0); 
filter.set("amount", amountMiddle, filter.animateIn - 1); } if 
(filter.animateOut > 0) { filter.set("amount", amountMiddle, filter.duration - 
filter.animateOut); filter.set("amount", amountEnd, filter.duration - 1); } } 
else if (!amountKeyframesButton.checked) { filter.resetProperty("amount"); 
filter.set("amount", amountMiddle); } else if (position !== null) { 
filter.set("amount", value, position); } }
+    function updateProperty_interpolation () { if (blockUpdate) return; var 
value = interpolationComboBox.currentIndex; filter.set("interpolation", value); 
}
+
+    function getPosition() {
+        return Math.max(producer.position - (filter.in - producer.in), 0)
+    }
+
+    GridLayout {
+        columns: 4
+        anchors.fill: parent
+        anchors.margins: 8
+
+        Label {
+            text: qsTr('Preset')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.Preset {
+            id: preset
+            parameters: ["yaw", "pitch", "roll", "fov", "interpolation", 
"amount"]
+            Layout.columnSpan: 3
+            onBeforePresetLoaded: {
+                filter.resetProperty('yaw')
+                filter.resetProperty('pitch')
+                filter.resetProperty('roll')
+                filter.resetProperty('fov')
+                filter.resetProperty('amount')
+                filter.resetProperty('interpolation')
+            }
+            onPresetSelected: {
+                yawMiddle = filter.getDouble("yaw", filter.animateIn); if 
(filter.animateIn > 0) { yawStart = filter.getDouble("yaw", 0); } if 
(filter.animateOut > 0) { yawEnd = filter.getDouble("yaw", filter.duration - 
1); }
+                pitchMiddle = filter.getDouble("pitch", filter.animateIn); if 
(filter.animateIn > 0) { pitchStart = filter.getDouble("pitch", 0); } if 
(filter.animateOut > 0) { pitchEnd = filter.getDouble("pitch", filter.duration 
- 1); }
+                rollMiddle = filter.getDouble("roll", filter.animateIn); if 
(filter.animateIn > 0) { rollStart = filter.getDouble("roll", 0); } if 
(filter.animateOut > 0) { rollEnd = filter.getDouble("roll", filter.duration - 
1); }
+                fovMiddle = filter.getDouble("fov", filter.animateIn); if 
(filter.animateIn > 0) { fovStart = filter.getDouble("fov", 0); } if 
(filter.animateOut > 0) { fovEnd = filter.getDouble("fov", filter.duration - 
1); }
+                amountMiddle = filter.getDouble("amount", filter.animateIn); 
if (filter.animateIn > 0) { amountStart = filter.getDouble("amount", 0); } if 
(filter.animateOut > 0) { amountEnd = filter.getDouble("amount", 
filter.duration - 1); }
+                interpolationValue = filter.get("interpolation");
+                setControls(null);
+            }
+        }
+
+        Label {
+            text: qsTr('Interpolation')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.ComboBox {
+            currentIndex: 0
+            implicitWidth: 180
+            model: ["Nearest-neighbor", "Bilinear"]
+            id: interpolationComboBox
+            Layout.columnSpan: 2
+            onCurrentIndexChanged: updateProperty_interpolation()
+        }
+        Shotcut.UndoButton {
+            id: interpolationUndo
+            onClicked: interpolationComboBox.currentIndex = 0
+        }
+
+        Label {
+            text: qsTr('Yaw')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.SliderSpinner {
+            id: yawSlider
+            minimumValue: -360
+            maximumValue: 360
+            spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1;
+            onValueChanged: updateProperty_yaw(getPosition())
+        }
+        Shotcut.KeyframesButton { id: yawKeyframesButton; onToggled: { var 
value = yawSlider.value; if (checked) { blockUpdate = true; if 
(filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("yaw"); 
yawSlider.enabled = true; } filter.clearSimpleAnimation("yaw"); blockUpdate = 
false; filter.set("yaw", value, getPosition()); } else { 
filter.resetProperty("yaw"); filter.set("yaw", value); } } }
+        Shotcut.UndoButton {
+            id: yawUndo
+            onClicked: yawSlider.value = 0
+        }
+
+        Label {
+            text: qsTr('Pitch')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.SliderSpinner {
+            id: pitchSlider
+            minimumValue: -180
+            maximumValue: 180
+            spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1;
+            onValueChanged: updateProperty_pitch(getPosition())
+        }
+        Shotcut.KeyframesButton { id: pitchKeyframesButton; onToggled: { var 
value = pitchSlider.value; if (checked) { blockUpdate = true; if 
(filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("pitch"); pitchSlider.enabled = true; } 
filter.clearSimpleAnimation("pitch"); blockUpdate = false; filter.set("pitch", 
value, getPosition()); } else { filter.resetProperty("pitch"); 
filter.set("pitch", value); } } }
+        Shotcut.UndoButton {
+            id: pitchUndo
+            onClicked: pitchSlider.value = -90
+        }
+
+        Label {
+            text: qsTr('Roll')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.SliderSpinner {
+            id: rollSlider
+            minimumValue: -180
+            maximumValue: 180
+            spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1;
+            onValueChanged: updateProperty_roll(getPosition())
+        }
+        Shotcut.KeyframesButton { id: rollKeyframesButton; onToggled: { var 
value = rollSlider.value; if (checked) { blockUpdate = true; if 
(filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("roll"); 
rollSlider.enabled = true; } filter.clearSimpleAnimation("roll"); blockUpdate = 
false; filter.set("roll", value, getPosition()); } else { 
filter.resetProperty("roll"); filter.set("roll", value); } } }
+        Shotcut.UndoButton {
+            id: rollUndo
+            onClicked: rollSlider.value = 0
+        }
+
+        Label {
+            text: qsTr('FOV')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.SliderSpinner {
+            id: fovSlider
+            minimumValue: 0
+            maximumValue: 180
+            spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1;
+            onValueChanged: updateProperty_fov(getPosition())
+        }
+        Shotcut.KeyframesButton { id: fovKeyframesButton; onToggled: { var 
value = fovSlider.value; if (checked) { blockUpdate = true; if 
(filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("fov"); 
fovSlider.enabled = true; } filter.clearSimpleAnimation("fov"); blockUpdate = 
false; filter.set("fov", value, getPosition()); } else { 
filter.resetProperty("fov"); filter.set("fov", value); } } }
+        Shotcut.UndoButton {
+            id: fovUndo
+            onClicked: fovSlider.value = 160
+        }
+
+        Label {
+            text: qsTr('Amount')
+            Layout.alignment: Qt.AlignRight
+        }
+        Shotcut.SliderSpinner {
+            id: amountSlider
+            minimumValue: 0
+            maximumValue: 100
+            suffix: ' %'
+            decimals: 0
+            stepSize: 1
+            onValueChanged: updateProperty_amount(getPosition())
+        }
+        Shotcut.KeyframesButton { id: amountKeyframesButton; onToggled: { var 
value = amountSlider.value; if (checked) { blockUpdate = true; if 
(filter.animateIn > 0 || filter.animateOut > 0) { 
filter.resetProperty("amount"); amountSlider.enabled = true; } 
filter.clearSimpleAnimation("amount"); blockUpdate = false; 
filter.set("amount", value, getPosition()); } else { 
filter.resetProperty("amount"); filter.set("amount", value); } } }
+        Shotcut.UndoButton {
+            id: amountUndo
+            onClicked: amountSlider.value = 100
+        }
+        Item {
+            Layout.fillHeight: true
+        }
+    }
+
+    Connections {
+        target: filter
+        onPropertyChanged: setControls()
+    }
+
+    Connections {
+        target: producer
+        onPositionChanged: setControls()
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml 
new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml
--- old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml   
1970-01-01 01:00:00.000000000 +0100
+++ new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml   
2021-09-13 07:05:06.000000000 +0200
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021 Meltytech, LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.1
+import Shotcut.Controls 1.0 as Shotcut
+
+Shotcut.VuiBase {
+    property var keyframableParameters: ['yaw', 'pitch', 'roll', 'fov']
+    property var startValues: []
+    property var middleValues: []
+    property var endValues: []
+
+    Connections {
+        target: filter
+        onChanged: mouseArea.enabled = filter.get('disable') !== '1'
+    }
+
+    MouseArea {
+        id: mouseArea
+        anchors.fill: parent
+
+        property double startYaw
+        property double startPitch
+        property double startRoll
+        property int startX
+        property int startY
+        onPressed: {
+            startX = mouse.x
+            startY = mouse.y
+            startYaw = filter.getDouble('yaw', getPosition())
+            startPitch = filter.getDouble('pitch', getPosition())
+            startRoll = filter.getDouble('roll', getPosition())
+        }
+        onPositionChanged: {
+            if (mouse.modifiers == Qt.ControlModifier) {
+                updateProperty('roll', clamp(startRoll + 0.5 * (startY - 
mouse.y), -180, 180))
+            } else {
+                updateProperty('yaw', clamp(startYaw + 0.2 * (startX - 
mouse.x), -360, 360))
+                updateProperty('pitch', clamp(startPitch + 0.1 * (mouse.y - 
startY), -180, 180))
+            }
+        }
+        onWheel: {
+            var fov = filter.getDouble('fov', getPosition())
+            updateProperty('fov', clamp(fov + 0.03 * wheel.angleDelta.y, 0, 
180))
+        }
+    }
+
+    function clamp(x, min, max) {
+        return Math.max(min, Math.min(max, x))
+    }
+
+    function getPosition() {
+        return Math.max(producer.position - (filter.in - producer.in), 0)
+    }
+
+    function updateProperty(name, value) {
+        var index = keyframableParameters.indexOf(name)
+        var position = getPosition()
+
+        if (position !== null) {
+            if (position <= 0 && filter.animateIn > 0) {
+                startValues[index] = value
+            } else if (position >= filter.duration - 1 && filter.animateOut > 
0) {
+                endValues[index] = value
+            } else {
+                middleValues[index] = value
+            }
+        } if (filter.animateIn > 0 || filter.animateOut > 0) {
+            filter.resetProperty(name)
+            if (filter.animateIn > 0) {
+                filter.set(name, startValues[index], 0)
+                filter.set(name, middleValues[index], filter.animateIn - 1)
+            }
+            if (filter.animateOut > 0) {
+                filter.set(name, middleValues[index], filter.duration - 
filter.animateOut)
+                filter.set(name, endValues[index], filter.duration - 1)
+            }
+        } else if (filter.keyframeCount(name) <= 0) {
+            filter.resetProperty(name)
+            filter.set(name, middleValues[index])
+        } else if (position !== null) {
+            filter.set(name, value, position)
+        }
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/qml/filters/fadein_movit/ui.qml 
new/shotcut-21.09.13/src/qml/filters/fadein_movit/ui.qml
--- old/shotcut-21.08.29/src/qml/filters/fadein_movit/ui.qml    2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/qml/filters/fadein_movit/ui.qml    2021-09-13 
07:05:06.000000000 +0200
@@ -19,6 +19,7 @@
 import QtQuick.Controls 2.12
 import QtQuick.Layouts 1.12
 import Shotcut.Controls 1.0 as Shotcut
+import org.shotcut.qml 1.0
 
 Item {
     width: 100
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/shotcut-21.08.29/src/qml/filters/fadeout_movit/ui.qml 
new/shotcut-21.09.13/src/qml/filters/fadeout_movit/ui.qml
--- old/shotcut-21.08.29/src/qml/filters/fadeout_movit/ui.qml   2021-08-29 
07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/qml/filters/fadeout_movit/ui.qml   2021-09-13 
07:05:06.000000000 +0200
@@ -20,6 +20,7 @@
 import QtQuick.Controls 2.12
 import QtQuick.Layouts 1.12
 import Shotcut.Controls 1.0 as Shotcut
+import org.shotcut.qml 1.0
 
 Item {
     width: 100
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/util.cpp 
new/shotcut-21.09.13/src/util.cpp
--- old/shotcut-21.08.29/src/util.cpp   2021-08-29 07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/util.cpp   2021-09-13 07:05:06.000000000 +0200
@@ -164,13 +164,13 @@
 
 static inline bool isValidGoProFirstFilePrefix(const QFileInfo& info)
 {
-    QStringList list {"GOPR", "GH01", "GS01", "GX01"};
+    QStringList list {"GOPR", "GH01", "GL01", "GM01", "GS01", "GX01"};
     return list.contains(info.baseName().left(4).toUpper());
 }
 
 static inline bool isValidGoProPrefix(const QFileInfo& info)
 {
-    QStringList list {"GP", "GH", "GS", "GX"};
+    QStringList list {"GP", "GH", "GL", "GM", "GS", "GX"};
     return list.contains(info.baseName().left(2).toUpper());
 }
 
@@ -544,3 +544,25 @@
     gcd = m;
     return gcd;
 }
+
+void Util::normalizeFrameRate(double fps, int& numerator, int& denominator)
+{
+    // Convert some common non-integer frame rates to fractions.
+    if (qRound(fps * 1000000.0) == 23976024) {
+        numerator = 24000;
+        denominator = 1001;
+    } else if (qRound(fps * 100000.0) == 2997003) {
+        numerator = 30000;
+        denominator = 1001;
+    } else if (qRound(fps * 1000000.0) == 47952048) {
+        numerator = 48000;
+         denominator = 1001;
+    } else if (qRound(fps * 100000.0) == 5994006) {
+        numerator = 60000;
+        denominator = 1001;
+    } else {
+        // Workaround storing QDoubleSpinBox::value() loses precision.
+        numerator = qRound(fps * 1000000.0);
+        denominator = 1000000;
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/shotcut-21.08.29/src/util.h 
new/shotcut-21.09.13/src/util.h
--- old/shotcut-21.08.29/src/util.h     2021-08-29 07:29:22.000000000 +0200
+++ new/shotcut-21.09.13/src/util.h     2021-09-13 07:05:06.000000000 +0200
@@ -60,6 +60,7 @@
     static bool isMemoryLow();
     static QString removeQueryString(const QString& s);
     static int greatestCommonDivisor(int m, int n);
+    static void normalizeFrameRate(double fps, int& numerator, int& 
denominator);
 };
 
 #endif // UTIL_H

Reply via email to