]> git.sesse.net Git - kdenlive/commitdiff
Rotoscoping: import keyframes generated by tracker.
authorTill Theato <root@ttill.de>
Sun, 8 May 2011 09:21:53 +0000 (09:21 +0000)
committerTill Theato <root@ttill.de>
Sun, 8 May 2011 09:21:53 +0000 (09:21 +0000)
Requires https://github.com/ttill/MLT-roto-tracking

svn path=/trunk/kdenlive/; revision=5571

effects/rotoscoping.xml
src/effectstackedit.cpp
src/effectstackedit.h
src/effectstackview.cpp
src/renderer.cpp
src/renderer.h
src/rotoscoping/rotowidget.cpp
src/rotoscoping/rotowidget.h
src/transitionsettings.cpp

index ba26ceb8acb9a2e25eaf614eaecadbf9c1bc90ef..61ed511d610e94276f371606f925931098a3dec9 100644 (file)
         <parameter type="bool" name="invert" default="0">
             <name>Invert</name>
         </parameter>
+        
+        <parameter type="bool" name="track" default="0">
+            <name>Track</name>
+        </parameter>
 
         <parameter type="constant" name="feather" max="500" min="0" default="0">
             <name>Feather width</name>
index 20931efae075587a34776dcf02648d2b7911141c..0b1621e64b5250af1bf020d15df50d93395403e6 100644 (file)
@@ -205,14 +205,14 @@ void EffectStackEdit::updateParameter(const QString &name, const QString &value)
     }
 }
 
-void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, int out, bool isEffect)
+void EffectStackEdit::transferParamDesc(const QDomElement d, ItemInfo info, bool isEffect)
 {
     clearAllItems();
     if (m_keyframeEditor) delete m_keyframeEditor;
     m_keyframeEditor = NULL;
     m_params = d;
-    m_in = in;
-    m_out = out;
+    m_in = isEffect ? info.cropStart.frames(KdenliveSettings::project_fps()) : info.startPos.frames(KdenliveSettings::project_fps());
+    m_out = isEffect ? (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1 : info.endPos.frames(KdenliveSettings::project_fps());
     if (m_params.isNull()) {
 //         kDebug() << "// EMPTY EFFECT STACK";
         return;
@@ -327,7 +327,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
             connect(pl, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
         } else if (type == "geometry") {
             if (KdenliveSettings::on_monitor_effects()) {
-                m_geometryWidget = new GeometryWidget(m_monitor, m_timecode, pos, isEffect, m_params.hasAttribute("showrotation"), this);
+                m_geometryWidget = new GeometryWidget(m_monitor, m_timecode, isEffect ? 0 : qMax(0, (int)info.startPos.frames(KdenliveSettings::project_fps())), isEffect, m_params.hasAttribute("showrotation"), this);
                 m_geometryWidget->setFrameSize(m_frameSize);
                 m_geometryWidget->slotShowScene(!disable);
                 // connect this before setupParam to make sure the monitor scene shows up at startup
@@ -343,7 +343,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
                 connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
                 connect(this, SIGNAL(effectStateChanged(bool)), m_geometryWidget, SLOT(slotShowScene(bool)));
             } else {
-                Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, pos);
+                Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, isEffect ? 0 : qMax(0, (int)info.startPos.frames(KdenliveSettings::project_fps())));
                 if (minFrame == maxFrame)
                     geo->setupParam(pa, m_in, m_out);
                 else
@@ -446,7 +446,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
 #ifdef QJSON
         } else if (type == "roto-spline") {
-            RotoWidget *roto = new RotoWidget(value, m_monitor, m_in, m_out, m_timecode, this);
+            RotoWidget *roto = new RotoWidget(value, m_monitor, info, m_timecode, this);
             roto->slotShowScene(!disable);
             connect(roto, SIGNAL(valueChanged()), this, SLOT(collectAllParameters()));
             connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
index 3fbd704140a127d68d1aa4077c7fdaf515784451..88688c75981d819415397768a66e42717acf39f4 100644 (file)
@@ -85,7 +85,7 @@ private:
 
 public slots:
     /** @brief Called when an effect is selected, builds the UI for this effect. */
-    void transferParamDesc(const QDomElement d, int pos, int in, int out, bool isEffect = true);
+    void transferParamDesc(const QDomElement d, ItemInfo info, bool isEffect = true);
 
     /** @brief Called whenever(?) some parameter is changed in the gui.
      *
index 6685df5dee119847ffc5f98123ec839ee7073f14..af514479af4654a6f03c95ad080360e003c7af25 100644 (file)
@@ -199,7 +199,8 @@ void EffectStackView::slotClipItemSelected(ClipItem* c, int ix)
     if (m_clipref == NULL) {
         m_ui.effectlist->blockSignals(true);
         m_ui.effectlist->clear();
-        m_effectedit->transferParamDesc(QDomElement(), 0, 0, 0);
+        ItemInfo info;
+        m_effectedit->transferParamDesc(QDomElement(), info);
         //m_ui.region_url->clear();
         m_ui.effectlist->blockSignals(false);
         m_ui.checkAll->setToolTip(QString());
@@ -302,7 +303,8 @@ void EffectStackView::setupListView(int ix)
     }
     m_ui.effectlist->blockSignals(false);
     if (m_ui.effectlist->count() == 0) {
-        m_effectedit->transferParamDesc(QDomElement(), 0, 0, 0);
+        ItemInfo info;
+        m_effectedit->transferParamDesc(QDomElement(), info);
         //m_ui.region_url->clear();
     } else slotItemSelectionChanged(false);
     slotUpdateCheckAllButton();
@@ -319,11 +321,16 @@ void EffectStackView::slotItemSelectionChanged(bool update)
         eff = m_currentEffectList.at(activeRow);
         if (m_trackMode) {
             // showing track effects
-            m_effectedit->transferParamDesc(eff, 0, 0, m_trackInfo.duration);
-        } else m_effectedit->transferParamDesc(eff,
-                                                   0,
-                                                   m_clipref->cropStart().frames(KdenliveSettings::project_fps()),
-                                                   (m_clipref->cropStart() + m_clipref->cropDuration()).frames(KdenliveSettings::project_fps()) - 1); //minx max frame
+            ItemInfo info;
+            info.track = m_trackInfo.type;
+            info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
+            info.cropStart = GenTime(0);
+            info.startPos = GenTime(-1);
+            info.track = 0;
+            m_effectedit->transferParamDesc(eff, info);
+        } else {
+            m_effectedit->transferParamDesc(eff, m_clipref->info());
+        }
         //m_ui.region_url->setUrl(KUrl(eff.attribute("region")));
         m_ui.labelComment->setText(i18n(eff.firstChildElement("description").firstChildElement("full").text().toUtf8().data()));
     }
@@ -386,11 +393,17 @@ void EffectStackView::slotResetEffect()
         if (m_trackMode) {
             EffectsList::setParameter(dom, "in", QString::number(0));
             EffectsList::setParameter(dom, "out", QString::number(m_trackInfo.duration));
-            m_effectedit->transferParamDesc(dom, 0, 0, m_trackInfo.duration);//minx max frame
+            ItemInfo info;
+            info.track = m_trackInfo.type;
+            info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
+            info.cropStart = GenTime(0);
+            info.startPos = GenTime(-1);
+            info.track = 0;
+            m_effectedit->transferParamDesc(dom, info);
             emit updateEffect(NULL, m_trackindex, old, dom, activeRow);
         } else {
             m_clipref->initEffect(dom);
-            m_effectedit->transferParamDesc(dom, 0, m_clipref->cropStart().frames(KdenliveSettings::project_fps()), (m_clipref->cropStart() + m_clipref->cropDuration()).frames(KdenliveSettings::project_fps()));//minx max frame
+            m_effectedit->transferParamDesc(dom, m_clipref->info());
             //m_ui.region_url->setUrl(KUrl(dom.attribute("region")));
             emit updateEffect(m_clipref, -1, old, dom, activeRow);
         }
@@ -417,7 +430,8 @@ void EffectStackView::clear()
     m_ui.buttonUp->setEnabled(false);
     m_ui.buttonDown->setEnabled(false);
     m_ui.checkAll->setEnabled(false);
-    m_effectedit->transferParamDesc(QDomElement(), 0, 0, 0);
+    ItemInfo info;
+    m_effectedit->transferParamDesc(QDomElement(), info);
     //m_ui.region_url->clear();
     m_ui.buttonShowComments->setEnabled(false);
     m_ui.labelComment->setText(QString());
index 9edc697aa1821c4e4dd3850a72ba85973b47650b..bfaa2f107da61b72478d9a9893043c22c5a54751 100644 (file)
@@ -4162,6 +4162,11 @@ void Render::sendFrameUpdate()
     }
 }
 
+Mlt::Producer* Render::getProducer()
+{
+    return m_mltProducer;
+}
+
 
 #include "renderer.moc"
 
index 191a21cc687a05ba94cec86d0931897f32f49298..b20afea559e09d92264e583824bed2cc51ade133 100644 (file)
@@ -265,6 +265,9 @@ Q_OBJECT public:
     QList <int> checkTrackSequence(int);
     void sendFrameUpdate();
 
+    /** @brief Returns a pointer to the main producer. */
+    Mlt::Producer *getProducer();
+
 private:
 
     /** @brief The name of this renderer.
index 9be8dcc7c2bbd2a917ae71459d295a5b89582f62..415b5e82f7043374febcfa5a9e57836cad38db34 100644 (file)
@@ -26,6 +26,8 @@
 #include "simplekeyframes/simplekeyframewidget.h"
 #include "kdenlivesettings.h"
 
+#include <mlt++/Mlt.h>
+
 #include <math.h>
 
 #include <qjson/parser.h>
 
 #include <QVBoxLayout>
 
+/** @brief Listener for "tracking-finished" event in MLT rotoscoping filter. */
+void tracking_finished(mlt_service *owner, RotoWidget *self, char *data)
+{
+    Q_UNUSED(owner)
 
-RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent) :
+    if (self)
+        self->setSpline(QString(data));
+}
+
+RotoWidget::RotoWidget(QString data, Monitor *monitor, ItemInfo info, Timecode t, QWidget* parent) :
         QWidget(parent),
         m_monitor(monitor),
         m_showScene(true),
-        m_in(in),
-        m_out(out)
+        m_in(info.cropStart.frames(KdenliveSettings::project_fps())),
+        m_out((info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1),
+        m_filter(NULL)
 {
     QVBoxLayout *l = new QVBoxLayout(this);
     m_keyframeWidget = new SimpleKeyframeWidget(t, m_out - m_in, this);
@@ -49,40 +60,6 @@ RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode
     edit->showVisibilityButton(true);
     m_scene = edit->getScene();
 
-    QJson::Parser parser;
-    bool ok;
-    m_data = parser.parse(data.toUtf8(), &ok);
-    if (!ok) {
-        // :(
-    }
-
-    
-    if (m_data.canConvert(QVariant::Map)) {
-        /*
-         * pass keyframe data to keyframe timeline
-         */
-        QList <int> keyframes;
-        QMap <QString, QVariant> map = m_data.toMap();
-        QMap <QString, QVariant>::const_iterator i = map.constBegin();
-        while (i != map.constEnd()) {
-            keyframes.append(i.key().toInt() - m_in);
-            ++i;
-        }
-        m_keyframeWidget->setKeyframes(keyframes);
-
-        for (int j = 0; j < keyframes.count(); ++j) {
-            // key might already be justified
-            if (map.contains(QString::number(keyframes.at(j) + m_in))) {
-                QVariant value = map.take(QString::number(keyframes.at(j) + m_in));
-                map[QString::number(keyframes.at(j) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = value;
-            }
-        }
-        m_data = QVariant(map);
-    } else {
-        // static (only one keyframe)
-        m_keyframeWidget->setKeyframes(QList <int>() << 0);
-    }
-
     m_item = new SplineItem(QList <BPoint>(), NULL, m_scene);
 
     connect(m_item, SIGNAL(changed(bool)), this, SLOT(slotUpdateData(bool)));
@@ -94,11 +71,15 @@ RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode
     connect(m_keyframeWidget, SIGNAL(keyframeMoved(int,int)), this, SLOT(slotMoveKeyframe(int,int)));
     connect(m_scene, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe()));
 
-    slotPositionChanged(0, false);
+    setSpline(data, false);
+    setupTrackingListen(info);
 }
 
 RotoWidget::~RotoWidget()
 {
+    if (m_filter)
+        mlt_events_disconnect(m_filter->get_properties(), this);
+
     delete m_keyframeWidget;
 
     m_scene->removeItem(m_item);
@@ -256,6 +237,11 @@ QList <BPoint> RotoWidget::getPoints(int keyframe)
         data = m_data.toMap()[QString::number(keyframe).rightJustified(log10((double)m_out) + 1, '0')].toList();
     else
         data = m_data.toList();
+
+    // skip tracking flag
+    if (data.count() && data.at(0).canConvert(QVariant::String))
+        data.removeFirst();
+
     foreach (const QVariant &bpoint, data) {
         QList <QVariant> l = bpoint.toList();
         BPoint p;
@@ -321,6 +307,76 @@ void RotoWidget::updateTimecodeFormat()
     m_keyframeWidget->updateTimecodeFormat();
 }
 
+void RotoWidget::keyframeTimelineFullUpdate()
+{
+    if (m_data.canConvert(QVariant::Map)) {
+        QList <int> keyframes;
+        QMap <QString, QVariant> map = m_data.toMap();
+        QMap <QString, QVariant>::const_iterator i = map.constBegin();
+        while (i != map.constEnd()) {
+            keyframes.append(i.key().toInt() - m_in);
+            ++i;
+        }
+        m_keyframeWidget->setKeyframes(keyframes);
+
+        /*for (int j = 0; j < keyframes.count(); ++j) {
+            // key might already be justified
+            if (map.contains(QString::number(keyframes.at(j) + m_in))) {
+                QVariant value = map.take(QString::number(keyframes.at(j) + m_in));
+                map[QString::number(keyframes.at(j) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = value;
+            }
+        }
+        m_data = QVariant(map);*/
+    } else {
+        // static (only one keyframe)
+        m_keyframeWidget->setKeyframes(QList <int>() << 0);
+    }
+}
+
+void RotoWidget::setupTrackingListen(ItemInfo info)
+{
+    if (info.startPos < GenTime()) {
+        // TODO: track effects
+        return;
+    }
+
+    Mlt::Service service(m_monitor->render->getProducer()->parent().get_service());
+    Mlt::Tractor tractor(service);
+    Mlt::Producer trackProducer(tractor.track(tractor.count() - info.track - 1));
+    Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+
+    Mlt::Producer *clip = trackPlaylist.get_clip_at((int)info.startPos.frames(KdenliveSettings::project_fps()));
+    if (!clip) {
+        return;
+    }
+
+    int i = 0;
+    Mlt::Filter *filter = clip->filter(0);
+    while (filter) {
+        if (strcmp(filter->get("kdenlive_id"), "rotoscoping") == 0) {
+            m_filter = filter;
+            filter->listen("tracking-finished", this, (mlt_listener)tracking_finished);
+            break;
+        }
+        filter = clip->filter(++i);
+    }
+
+    delete clip;
+}
+
+void RotoWidget::setSpline(QString spline, bool notify)
+{
+    QJson::Parser parser;
+    bool ok;
+    m_data = parser.parse(spline.simplified().toUtf8(), &ok);
+    if (!ok) {
+        // :(
+    }
+    keyframeTimelineFullUpdate();
+    slotPositionChanged(m_keyframeWidget->getPosition(), false);
+    if (notify)
+        emit valueChanged();
+}
 
 
 static QVariant interpolate(int position, int in, int out, QVariant *splineIn, QVariant *splineOut)
@@ -329,7 +385,12 @@ static QVariant interpolate(int position, int in, int out, QVariant *splineIn, Q
     QList<QVariant> keyframe1 = splineIn->toList();
     QList<QVariant> keyframe2 = splineOut->toList();
     QList<QVariant> keyframe;
+    if (keyframe1.count() && keyframe1.at(0).canConvert(QVariant::String))
+        keyframe1.removeFirst();
+    if (keyframe2.count() && keyframe2.at(0).canConvert(QVariant::String))
+        keyframe2.removeFirst();
     int max = qMin(keyframe1.count(), keyframe2.count());
+        
     for (int i = 0; i < max; ++i) {
         QList<QVariant> p1 = keyframe1.at(i).toList();
         QList<QVariant> p2 = keyframe2.at(i).toList();
index 90ba82e07a314fce42448daa64bf776800a6258a..7a0d2d9f7ffa332c1a90c2854ca8ba1761cbe721 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef ROTOWIDGET_H
 #define ROTOWIDGET_H
 
+#include "definitions.h"
 #include "bpoint.h"
 #include "timecode.h"
 
@@ -28,6 +29,10 @@ class Monitor;
 class MonitorScene;
 class SplineItem;
 class SimpleKeyframeWidget;
+namespace Mlt
+{
+class Filter;
+}
 
 /** @brief Adjusts keyframes after resizing a clip. */
 bool adjustRotoDuration(QString *data, int in, int out);
@@ -37,12 +42,15 @@ class RotoWidget : public QWidget
     Q_OBJECT
 
 public:
-    RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent = 0);
+    RotoWidget(QString data, Monitor *monitor, ItemInfo info, Timecode t, QWidget* parent = 0);
     virtual ~RotoWidget();
 
     /** @brief Returns the spline(s) in the JSON format used by filter_rotoscoping (MLT). */
     QString getSpline();
 
+    /** @brief Replaces current data with \param spline (JSON). */
+    void setSpline(QString spline, bool notify = true);
+
     /** @brief Passed on to the keyframe timeline. Switches between frames and hh:mm:ss:ff timecode. */
     void updateTimecodeFormat();
 
@@ -67,6 +75,7 @@ private:
     SplineItem *m_item;
     int m_in;
     int m_out;
+    Mlt::Filter *m_filter;
 
     /** @brief Returns the list of cubic Bézier points that form the spline at position @param keyframe.
      * The points are brought from the range [0, 1] into project resolution space.
@@ -74,6 +83,12 @@ private:
      * Set @param keyframe to -1 if only one keyframe currently exists. */
     QList <BPoint> getPoints(int keyframe);
 
+    /** @brief Adds tracking_finished as listener for "tracking-finished" event in MLT rotoscoping filter. */
+    void setupTrackingListen(ItemInfo info);
+
+    /** @brief Passes list of keyframe positions to keyframe timeline widget. */
+    void keyframeTimelineFullUpdate();
+
 private slots:
     /** @brief Makes sure the monitor effect scene is only visible if the clip this geometry belongs to is visible.
     * @param renderPos Postion of the Monitor / Timeline cursor */
index e7984f19d16bef67f80522e3fff315762d1fa6db..6d87559b27700b064b6f36c23e5d9a33aa8a6285 100644 (file)
@@ -105,21 +105,20 @@ void TransitionSettings::updateTrackList()
 void TransitionSettings::slotTransitionChanged(bool reinit, bool updateCurrent)
 {
     QDomElement e = m_usedTransition->toXML().cloneNode().toElement();
-    int start = m_usedTransition->startPos().frames(KdenliveSettings::project_fps());
-    int end = m_usedTransition->endPos().frames(KdenliveSettings::project_fps());
     if (reinit) {
         // Reset the transition parameters to the default one
         QDomElement newTransition = MainWindow::transitions.getEffectByName(transitionList->currentText()).cloneNode().toElement();
         slotUpdateEffectParams(e, newTransition);
-        m_effectEdit->transferParamDesc(newTransition, start, start, end, false);
+        m_effectEdit->transferParamDesc(newTransition, m_usedTransition->info(), false);
     } else if (!updateCurrent) {
         // Transition changed, update parameters dialog
         //slotUpdateEffectParams(e, e);
-        m_effectEdit->transferParamDesc(e, start, start, end, false);
+        m_effectEdit->transferParamDesc(e, m_usedTransition->info(), false);
     } else {
         // Same transition, we just want to update the parameters value
         slotUpdateEffectParams(e, e);
-        if (m_usedTransition->hasGeometry()) m_effectEdit->transferParamDesc(m_usedTransition->toXML(), start, start, end, false);
+        if (m_usedTransition->hasGeometry())
+            m_effectEdit->transferParamDesc(m_usedTransition->toXML(), m_usedTransition->info(), false);
     }
 }
 
@@ -183,7 +182,8 @@ void TransitionSettings::slotTransitionItemSelected(Transition* t, int nextTrack
     } else {
         // null transition selected
         m_usedTransition = NULL;
-        m_effectEdit->transferParamDesc(QDomElement(), 0, 0, 0, false);
+        ItemInfo info;
+        m_effectEdit->transferParamDesc(QDomElement(), info, false);
     }
 
 }