[multimedia/kdenlive] src/dialogs: timeremap: serialize and parse keyframe types via MLT

Jean-Baptiste Mardelle <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 00855ef186ff875caa3d1bf90a81936491b5cf06 by Jean-Baptiste Mardelle, on behalf of Yash Bavadiya.
Committed on 18/08/2026 at 08:31.
Pushed by mardelle into branch 'master'.

timeremap: serialize and parse keyframe types via MLT

M  +36   -17   src/dialogs/timeremap.cpp
M  +1    -1    src/dialogs/timeremap.h

https://invent.kde.org/multimedia/kdenlive/-/commit/00855ef186ff875caa3d1bf90a81936491b5cf06

diff --git a/src/dialogs/timeremap.cpp b/src/dialogs/timeremap.cpp
index ac3d648da4..129cc91ea7 100644
--- a/src/dialogs/timeremap.cpp
+++ b/src/dialogs/timeremap.cpp
@@ -306,15 +306,26 @@ void RemapView::loadKeyframes(const QString &mapData)
         Q_EMIT atKeyframe(true, true);
         Q_EMIT updateKeyframes(false);
     } else {
-        QStringList str = mapData.split(QLatin1Char(';'));
-        for (auto &s : str) {
-            int pos = m_service->time_to_frames(s.section(QLatin1Char('='), 0, 0).toUtf8().constData());
-            int val = GenTime(s.section(QLatin1Char('='), 1).toDouble()).frames(pCore->getCurrentFps());
+        Mlt::Properties props;
+        props.set("_profile", pCore->getProjectProfile().get_profile(), 0);
+        props.set("key", mapData.toUtf8().constData());
+        // This is a fake query to force the animation to be parsed
+        (void)props.anim_get_double("key", 0);
+        std::shared_ptr<Mlt::Animation> anim(props.get_anim("key"));
+        int kfrCount = anim->key_count();
+        for (int ix = 0; ix < kfrCount; ix++) {
+            int pos = 0;
+            mlt_keyframe_type type = mlt_keyframe_linear;
+            anim->key_get(ix, pos, type);
+            int val = GenTime(props.anim_get_double("key", pos)).frames(pCore->getCurrentFps());
             // HACK: we always set last keyframe 1 frame after in MLT to ensure we have a correct last frame
-            if (s == str.constLast()) {
+            if (ix == kfrCount - 1) {
                 pos--;
             }
             m_keyframes.insert(pos, val);
+            if (type != mlt_keyframe_linear) {
+                m_keyframeTypes.insert(pos, type);
+            }
             m_duration = qMax(m_duration, pos - m_inFrame);
             m_duration = qMax(m_duration, val - m_inFrame);
         }
@@ -1261,25 +1272,33 @@ void RemapView::updateAfterSpeed(double speed)
     }
 }
 
-const QString RemapView::getKeyframesData(QMap<int, int> keyframes) const
+const QString RemapView::getKeyframesData(QMap<int, int> keyframes, QMap<int, int> keyframeTypes) const
 {
-    QStringList result;
     if (keyframes.isEmpty()) {
         keyframes = m_keyframes;
+        keyframeTypes = m_keyframeTypes;
+    }
+    if (keyframes.isEmpty()) {
+        return QString();
     }
-    QMapIterator<int, int> i(keyframes);
-    int offset = 0;
     Mlt::Properties props;
     props.set("_profile", pCore->getProjectProfile().get_profile(), 0);
+    QMapIterator<int, int> i(keyframes);
+    int offset = 0;
     while (i.hasNext()) {
         i.next();
         if (i.key() == keyframes.lastKey()) {
             // HACK: we always set last keyframe 1 frame after in MLT to ensure we have a correct last frame
             offset = 1;
         }
-        result << QStringLiteral("%1=%2").arg(props.frames_to_time(i.key() + offset, mlt_time_clock)).arg(GenTime(i.value(), pCore->getCurrentFps()).seconds());
+        mlt_keyframe_type type = mlt_keyframe_type(keyframeTypes.value(i.key(), KeyframeType::Linear));
+        props.anim_set("key", GenTime(i.value(), pCore->getCurrentFps()).seconds(), i.key() + offset, 0, type);
     }
-    return result.join(QLatin1Char(';'));
+    std::shared_ptr<Mlt::Animation> anim(props.get_anim("key"));
+    char *data = anim->serialize_cut(mlt_time_clock);
+    const QString result(data);
+    free(data);
+    return result;
 }
 
 void RemapView::reloadProducer()
@@ -2048,12 +2067,12 @@ void TimeRemap::updateKeyframesWithUndo(const QMap<int, int> &updatedKeyframes,
         m_isUpdatingKeyframes = false;
     }
 
-    Fun local_undo = [this, link = m_remapLink, splitLink = m_splitRemap, previousKeyframes, cid = m_cid, oldIn = m_view->m_oldInFrame, hadPitch, splitHadPitch,
-                      masterIsAudio, splitIsAudio, hadBlend]() {
+    Fun local_undo = [this, link = m_remapLink, splitLink = m_splitRemap, previousKeyframes, previousTypes = m_view->m_keyframeTypesOrigin, cid = m_cid,
+                      oldIn = m_view->m_oldInFrame, hadPitch, splitHadPitch, masterIsAudio, splitIsAudio, hadBlend]() {
         QString oldKfData;
         bool keyframesChanged = false;
         if (!previousKeyframes.isEmpty()) {
-            oldKfData = m_view->getKeyframesData(previousKeyframes);
+            oldKfData = m_view->getKeyframesData(previousKeyframes, previousTypes);
             keyframesChanged = true;
         }
         if (keyframesChanged) {
@@ -2090,12 +2109,12 @@ void TimeRemap::updateKeyframesWithUndo(const QMap<int, int> &updatedKeyframes,
         return true;
     };
 
-    Fun local_redo = [this, link = m_remapLink, splitLink = m_splitRemap, updatedKeyframes, cid = m_cid, usePitch, masterIsAudio, splitIsAudio,
-                      in = m_view->m_inFrame, useBlend]() {
+    Fun local_redo = [this, link = m_remapLink, splitLink = m_splitRemap, updatedKeyframes, updatedTypes = m_view->m_keyframeTypes, cid = m_cid, usePitch,
+                      masterIsAudio, splitIsAudio, in = m_view->m_inFrame, useBlend]() {
         QString newKfData;
         bool keyframesChanged = false;
         if (!updatedKeyframes.isEmpty()) {
-            newKfData = m_view->getKeyframesData(updatedKeyframes);
+            newKfData = m_view->getKeyframesData(updatedKeyframes, updatedTypes);
             keyframesChanged = true;
         }
         if (keyframesChanged) {
diff --git a/src/dialogs/timeremap.h b/src/dialogs/timeremap.h
index 9c847f3f2f..71720fafc2 100644
--- a/src/dialogs/timeremap.h
+++ b/src/dialogs/timeremap.h
@@ -38,7 +38,7 @@ public:
     void setBinClipDuration(std::shared_ptr<ProjectClip> clip, int duration);
     void setDuration(std::shared_ptr<Mlt::Producer> service, int duration, int sourceDuration = 0);
     void loadKeyframes(const QString &mapData);
-    const QString getKeyframesData(QMap<int,int> keyframes = {}) const;
+    const QString getKeyframesData(QMap<int, int> keyframes = {}, QMap<int, int> keyframeTypes = {}) const;
     int position() const;
     int remapDuration() const;
     int remapMax() const;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.