[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdenlive/refactoring_timeline] /: Steps towards better gestion of the master producers in the proje
From:       Nicolas Carion <null () kde ! org>
Date:       2018-04-30 22:26:41
Message-ID: E1fDHFp-0003w7-Fl () code ! kde ! org
[Download RAW message or body]

Git commit 3fc4464f474a5acecbed42ff70d390b0874f5ce0 by Nicolas Carion.
Committed on 30/04/2018 at 22:27.
Pushed by alcinos into branch 'refactoring_timeline'.

Steps towards better gestion of the master producers in the projectClip

M  +146  -19   src/bin/projectclip.cpp
M  +31   -4    src/bin/projectclip.h
M  +16   -0    src/definitions.cpp
M  +5    -3    src/definitions.h
M  +65   -93   src/effects/effectstack/model/effectstackmodel.cpp
M  +13   -6    src/effects/effectstack/model/effectstackmodel.hpp
M  +2    -8    src/mainwindow.cpp
M  +20   -39   src/mltcontroller/clipcontroller.cpp
M  +7    -7    src/mltcontroller/clipcontroller.h
M  +43   -8    src/timeline2/model/builders/meltBuilder.cpp
M  +69   -52   src/timeline2/model/clipmodel.cpp
M  +18   -12   src/timeline2/model/clipmodel.hpp
M  +8    -8    src/timeline2/model/timelinefunctions.cpp
M  +3    -3    src/timeline2/model/timelinefunctions.hpp
M  +2    -2    src/timeline2/model/timelineitemmodel.cpp
M  +3    -2    src/timeline2/model/timelinemodel.cpp
M  +1    -1    src/timeline2/model/timelinemodel.hpp
M  +2    -2    src/timeline2/view/timelinecontroller.cpp
M  +1    -1    src/timeline2/view/timelinecontroller.h
M  +2    -2    tests/groupstest.cpp
M  +31   -31   tests/modeltest.cpp
M  +2    -2    tests/regressions.cpp

https://commits.kde.org/kdenlive/3fc4464f474a5acecbed42ff70d390b0874f5ce0

diff --git a/src/bin/projectclip.cpp b/src/bin/projectclip.cpp
index f5504310e..b67acbb1d 100644
--- a/src/bin/projectclip.cpp
+++ b/src/bin/projectclip.cpp
@@ -28,9 +28,8 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #include "doc/kthumb.h"
 #include "effects/effectstack/model/effectstackmodel.hpp"
 #include "jobs/jobmanager.h"
-#include "jobs/thumbjob.hpp"
 #include "jobs/loadjob.hpp"
-#include <jobs/proxyclipjob.h>
+#include "jobs/thumbjob.hpp"
 #include "kdenlivesettings.h"
 #include "lib/audio/audioStreamInfo.h"
 #include "mltcontroller/clip.h"
@@ -49,6 +48,7 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #include "utils/thumbnailcache.hpp"
 #include "xml/xml.hpp"
 #include <QPainter>
+#include <jobs/proxyclipjob.h>
 #include <kimagecache.h>
 
 #include "kdenlive_debug.h"
@@ -77,7 +77,8 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, \
std::shared_ptr<  } else {
         m_thumbnail = thumb;
     }
-    if (m_clipType == ClipType::AV || m_clipType == ClipType::Audio || m_clipType == \
ClipType::Image || m_clipType == ClipType::Video || m_clipType == ClipType::Playlist \
|| m_clipType == ClipType::TextTemplate) { +    if (m_clipType == ClipType::AV || \
m_clipType == ClipType::Audio || m_clipType == ClipType::Image || m_clipType == \
ClipType::Video || +        m_clipType == ClipType::Playlist || m_clipType == \
ClipType::TextTemplate) {  pCore->bin()->addWatchFile(id, clipUrl());
     }
     // Make sure we have a hash for this clip
@@ -138,7 +139,8 @@ std::shared_ptr<ProjectClip> ProjectClip::construct(const QString \
&id, const QDo  ProjectClip::~ProjectClip()
 {
     // controller is deleted in bincontroller
-    if (m_clipType == ClipType::AV || m_clipType == ClipType::Audio || m_clipType == \
ClipType::Image || m_clipType == ClipType::Video || m_clipType == ClipType::Playlist \
|| m_clipType == ClipType::TextTemplate) { +    if (m_clipType == ClipType::AV || \
m_clipType == ClipType::Audio || m_clipType == ClipType::Image || m_clipType == \
ClipType::Video || +        m_clipType == ClipType::Playlist || m_clipType == \
ClipType::TextTemplate) {  pCore->bin()->removeWatchFile(clipId(), clipUrl());
     }
     m_thumbMutex.lock();
@@ -146,11 +148,6 @@ ProjectClip::~ProjectClip()
     m_thumbMutex.unlock();
     m_thumbThread.waitForFinished();
     audioFrameCache.clear();
-    // delete all timeline producers
-    std::map<int, std::shared_ptr<Mlt::Producer>>::iterator itr = \
                m_timelineProducers.begin();
-    while (itr != m_timelineProducers.end()) {
-        itr = m_timelineProducers.erase(itr);
-    }
 }
 
 void ProjectClip::connectEffectStack()
@@ -301,7 +298,7 @@ void ProjectClip::reloadProducer(bool refreshOnly)
         pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadjobId, QString(), \
150, -1, true, true);  
     } else {
-        //TODO: check if another load job is running?
+        // TODO: check if another load job is running?
         QDomDocument doc;
         QDomElement xml = toXml(doc);
         if (!xml.isNull()) {
@@ -341,7 +338,8 @@ void ProjectClip::setThumbnail(const QImage &img)
     }
     m_thumbnail = QIcon(thumb);
     if (auto ptr = m_model.lock()) {
-        std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()), \
AbstractProjectItem::DataThumbnail); +        \
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()),
 +                                                                       \
AbstractProjectItem::DataThumbnail);  }
 }
 
@@ -379,11 +377,13 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> \
                producer, bool repl
         if (auto ptr = m_model.lock()) emit \
std::static_pointer_cast<ProjectItemModel>(ptr)->refreshPanel(m_binId);  }
     if (auto ptr = m_model.lock()) {
-        std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()), \
AbstractProjectItem::DataDuration); +        \
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()),
 +                                                                       \
AbstractProjectItem::DataDuration);  }
     // Make sure we have a hash for this clip
     getFileHash();
-    if (m_clipType == ClipType::AV || m_clipType == ClipType::Audio || m_clipType == \
ClipType::Image || m_clipType == ClipType::Video || m_clipType == ClipType::Playlist \
|| m_clipType == ClipType::TextTemplate) { +    if (m_clipType == ClipType::AV || \
m_clipType == ClipType::Audio || m_clipType == ClipType::Image || m_clipType == \
ClipType::Video || +        m_clipType == ClipType::Playlist || m_clipType == \
ClipType::TextTemplate) {  pCore->bin()->addWatchFile(clipId(), clipUrl());
     }
     // set parent again (some info need to be stored in producer)
@@ -405,7 +405,7 @@ std::shared_ptr<Mlt::Producer> ProjectClip::thumbProducer()
         return nullptr;
     }
     if (KdenliveSettings::gpu_accel()) {
-        //TODO: when the original producer changes, we must reload this thumb \
producer +        // TODO: when the original producer changes, we must reload this \
thumb producer  Clip clip(*prod.get());
         m_thumbsProducer = \
std::make_shared<Mlt::Producer>(clip.softClone(ClipController::getPassPropertiesList()));
  Mlt::Filter scaler(*prod->profile(), "swscale");
@@ -418,6 +418,127 @@ std::shared_ptr<Mlt::Producer> ProjectClip::thumbProducer()
     return m_thumbsProducer;
 }
 
+void ProjectClip::createVideoMasterProducer()
+{
+    if (!m_videoProducer) {
+        m_videoProducer = cloneProducer(&pCore->getCurrentProfile()->profile());
+        // disable audio but activate video
+        m_videoProducer->set("set.test_audio", 1);
+        m_videoProducer->set("set.test_image", 0);
+    }
+}
+std::shared_ptr<Mlt::Producer> ProjectClip::getTimelineProducer(int clipId, \
PlaylistState state, double speed) +{
+    if (qFuzzyCompare(speed, 1.0)) {
+        // we are requesting a normal speed producer
+        // We can first cleen the speed producers we have for the current id
+        m_timewarpProducers.erase(clipId);
+        if (state == PlaylistState::AudioOnly) {
+            // We need to get an audio producer, if none exists
+            if (m_audioProducers.count(clipId) == 0) {
+                m_audioProducers[clipId] = \
cloneProducer(&pCore->getCurrentProfile()->profile()); +                \
m_audioProducers[clipId]->set("set.test_audio", 0); +                \
m_audioProducers[clipId]->set("set.test_image", 1); +            }
+            return std::shared_ptr<Mlt::Producer>(m_audioProducers[clipId]->cut());
+        }
+        // we return the video producer
+        m_audioProducers.erase(clipId);
+        createVideoMasterProducer();
+        return std::shared_ptr<Mlt::Producer>(m_videoProducer->cut());
+    }
+
+    // in that case, we need to create a warp producer, if we don't have one
+    m_audioProducers.erase(clipId);
+
+    std::shared_ptr<Mlt::Producer> warpProducer;
+    if (m_timewarpProducers.count(clipId) > 0) {
+        if (qFuzzyCompare(m_timewarpProducers[clipId]->get_double("warp_speed"), \
speed)) { +            // the producer we have is good, use it !
+            warpProducer = m_timewarpProducers[clipId];
+        }
+    }
+    if (!warpProducer) {
+        QString resource = \
QString("timewarp:%1:%2").arg(speed).arg(originalProducer()->get("resource")); +      \
warpProducer.reset(new Mlt::Producer(*originalProducer()->profile(), \
resource.toUtf8().constData())); +    }
+
+    warpProducer->set("set.test_audio", 1);
+    warpProducer->set("set.test_image", 1);
+    if (state == PlaylistState::AudioOnly) {
+        warpProducer->set("set.test_audio", 0);
+    }
+    if (state == PlaylistState::VideoOnly) {
+        warpProducer->set("set.test_image", 0);
+    }
+    m_timewarpProducers[clipId] = warpProducer;
+    return warpProducer;
+}
+
+std::pair<std::shared_ptr<Mlt::Producer>, bool> \
ProjectClip::giveMasterAndGetTimelineProducer(int clipId, \
std::shared_ptr<Mlt::Producer> master, +                                              \
PlaylistState state) +{
+    int in = master->get_in();
+    int out = master->get_out();
+    if (master->parent().is_valid()) {
+        // in that case, we have a cut
+        // check whether it's a timewarp
+        double speed = 1.0;
+        double timeWarp = false;
+        if (QString::fromUtf8(master->parent().get("mlt_service")) == \
QLatin1String("timewarp")) { +            speed = \
master->parent().get_double("warp_speed"); +            timeWarp = true;
+        }
+        if (master->parent().get_int("loaded") == 1) {
+            // we already have a clip that shares the same master
+
+            if (state == PlaylistState::AudioOnly || timeWarp) {
+                // In that case, we must create copies
+                return {getTimelineProducer(clipId, state, speed), false};
+            }
+            // if it's a video or disabled clip, we must make sure that its master \
clip matches our video master +            if (!m_videoProducer) {
+                qDebug() << "Warning: weird, we found a video clip whose master is \
already loaded but we don't have any yet"; +                \
createVideoMasterProducer(); +                return \
{std::shared_ptr<Mlt::Producer>(m_videoProducer->cut(in, out)), false}; +            \
} +            if (QString::fromUtf8(m_videoProducer->get("id")) != \
QString::fromUtf8(master->parent().get("id"))) { +                qDebug() << \
"Warning: weird, we found a video clip whose master is already loaded but doesn't \
match ours"; +                return \
{std::shared_ptr<Mlt::Producer>(m_videoProducer->cut(in, out)), false}; +            \
} +            // We have a good id, this clip can be used
+            return {master, true};
+        } else {
+            master->parent().set("loaded", 1);
+            if (state == PlaylistState::AudioOnly) {
+                m_audioProducers[clipId] = \
std::shared_ptr<Mlt::Producer>(&master->parent()); +                return {master, \
true}; +            }
+            if (timeWarp) {
+                m_timewarpProducers[clipId] = \
std::shared_ptr<Mlt::Producer>(&master->parent()); +                return {master, \
true}; +            }
+            if (!m_videoProducer) {
+                // good, we found a master video producer, and we didn't have any
+                m_videoProducer.reset(&master->parent());
+                return {master, true};
+            }
+            qDebug() << "Warning: weird, we found a video clip whose master is not \
loaded but we already have a master"; +            return \
{std::shared_ptr<Mlt::Producer>(m_videoProducer->cut(in, out)), false}; +        }
+    } else if (master->is_valid()) {
+        // in that case, we have a master
+        qDebug() << "Warning: weird, we received a master clip in lieue of a cut";
+        double speed = 1.0;
+        if (QString::fromUtf8(master->get("mlt_service")) == \
QLatin1String("timewarp")) { +            speed = master->get_double("warp_speed");
+        }
+        return {getTimelineProducer(clipId, state, speed), false};
+    }
+    // we have a problem
+    return {std::shared_ptr<Mlt::Producer>(ClipController::mediaUnavailable->cut()), \
false}; +}
+/*
 std::shared_ptr<Mlt::Producer> \
ProjectClip::timelineProducer(PlaylistState::ClipState state, int track)  {
     if (!m_service.startsWith(QLatin1String("avformat"))) {
@@ -452,7 +573,7 @@ std::shared_ptr<Mlt::Producer> \
ProjectClip::timelineProducer(PlaylistState::Clip  std::shared_ptr<Mlt::Producer> \
normalProd = cloneProducer();  m_timelineProducers[track] = normalProd;
     return std::shared_ptr<Mlt::Producer>(normalProd->cut());
-}
+}*/
 
 std::shared_ptr<Mlt::Producer> ProjectClip::cloneProducer(Mlt::Profile *destProfile)
 {
@@ -554,7 +675,7 @@ const QString ProjectClip::getFileHash()
         break;
     }
     if (fileHash.isEmpty()) {
-        qDebug()<<"// WARNING EMPTY CLIP HASH: ";
+        qDebug() << "// WARNING EMPTY CLIP HASH: ";
         return QString();
     }
     QString result = fileHash.toHex();
@@ -585,7 +706,8 @@ void ProjectClip::setProperties(const QMap<QString, QString> \
&properties, bool r  if (properties.contains(QStringLiteral("templatetext"))) {
         m_description = properties.value(QStringLiteral("templatetext"));
         if (auto ptr = m_model.lock())
-            std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()), \
AbstractProjectItem::ClipStatus); +            \
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()),
 +                                                                           \
AbstractProjectItem::ClipStatus);  refreshPanel = true;
     }
     timelineProperties << QStringLiteral("force_aspect_ratio") << \
QStringLiteral("video_index") << QStringLiteral("audio_index") @@ -639,7 +761,8 @@ \
                void ProjectClip::setProperties(const QMap<QString, QString> \
                &properties, bool r
     if (properties.contains(QStringLiteral("length")) || \
properties.contains(QStringLiteral("kdenlive:duration"))) {  m_duration = \
getStringDuration();  if (auto ptr = m_model.lock())
-            std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()), \
AbstractProjectItem::DataDuration); +            \
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()),
 +                                                                           \
AbstractProjectItem::DataDuration);  refreshOnly = false;
         reload = true;
     }
@@ -648,7 +771,8 @@ void ProjectClip::setProperties(const QMap<QString, QString> \
&properties, bool r  m_name = properties.value(QStringLiteral("kdenlive:clipname"));
         refreshPanel = true;
         if (auto ptr = m_model.lock()) {
-            std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()), \
AbstractProjectItem::DataName); +            \
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()),
 +                                                                           \
AbstractProjectItem::DataName);  }
         // update timeline clips
         updateTimelineClips(QVector<int>() << TimelineModel::NameRole);
@@ -980,6 +1104,8 @@ bool ProjectClip::isIncludedInTimeline()
 
 void ProjectClip::updateChildProducers()
 {
+    // TODO refac: the effect should be managed by an effectstack on the master
+    /*
     // pass effect stack on all child producers
     QMutexLocker locker(&m_producerMutex);
     for (const auto &clip : m_timelineProducers) {
@@ -989,6 +1115,7 @@ void ProjectClip::updateChildProducers()
             clp.replaceEffects(*m_masterProducer);
         }
     }
+    */
 }
 
 void ProjectClip::replaceInTimeline()
diff --git a/src/bin/projectclip.h b/src/bin/projectclip.h
index 45210c2da..a46b438f5 100644
--- a/src/bin/projectclip.h
+++ b/src/bin/projectclip.h
@@ -49,7 +49,14 @@ class Properties;
 /**
  * @class ProjectClip
  * @brief Represents a clip in the project (not timeline).
- *
+ * It will be displayed as a bin item that can be dragged onto the timeline.
+ * A single bin clip can be inserted several times on the timeline, and the \
ProjectClip + * keeps track of all the ids of the corresponding ClipModel.
+ * Note that because of a limitation in melt and AvFilter, it is currently difficult \
to + * mix the audio of two producers that are cut from the same master producer
+ * (that produces small but noticeable clicking artifacts)
+ * To workaround this, we need to have a master clip for each instance of the audio \
clip in the timeline. This class is tracking them all. This track also holds + * a \
                master clip for each clip where the timewarp producer has been \
                applied
  */
 
 class ProjectClip : public AbstractProjectItem, public ClipController
@@ -140,7 +147,7 @@ public:
     bool isReady() const;
 
     /** @brief Returns this clip's producer. */
-    std::shared_ptr< Mlt::Producer > thumbProducer();
+    std::shared_ptr<Mlt::Producer> thumbProducer();
 
     /** @brief Recursively disable/enable bin effects. */
     void setBinEffectsEnabled(bool enabled) override;
@@ -188,7 +195,19 @@ public:
     bool isIncludedInTimeline() override;
     /** @brief Returns a list of all timeline clip ids for this bin clip */
     QList<int> timelineInstances() const;
-    std::shared_ptr<Mlt::Producer> timelineProducer(PlaylistState::ClipState state = \
PlaylistState::Original, int track = 1); +    /** @brief This function returns a cut \
to the master producer associated to the timeline clip with given ID. +        Each \
clip must have a different master producer (see comment of the class) +    */
+    std::shared_ptr<Mlt::Producer> getTimelineProducer(int clipId, PlaylistState st, \
double speed = 1.0); +
+    /* @brief This function should only be used at loading. It takes a producer that \
was read from mlt, and checks whether the master producer is already in +       use. \
If yes, then we must create a new one, because of the mixing bug. In any case, we \
return a cut of the master that can be used in the timeline The +       bool returned \
has the following sementic: +           - if true, then the returned cut still \
possibly has effect on it. You need to rebuild the effectStack based on this +        \
- if false, the the returned cut don't have effects anymore (it's a fresh one), so \
you need to reload effects from the old producer +    */
+    std::pair<std::shared_ptr<Mlt::Producer>, bool> \
giveMasterAndGetTimelineProducer(int clipId, std::shared_ptr<Mlt::Producer> master, \
PlaylistState state); +
     std::shared_ptr<Mlt::Producer> cloneProducer(Mlt::Profile *destProfile = \
nullptr);  void updateTimelineClips(QVector<int> roles);
 
@@ -234,8 +253,16 @@ private:
     const QString geometryWithOffset(const QString &data, int offset);
     void doExtractImage();
 
+    // This is a helper function that creates the video producer. This is a clone of \
the original one, with audio disabled +    void createVideoMasterProducer();
+
     std::map<int, std::weak_ptr<TimelineModel>> m_registeredClips;
-    std::map<int, std::shared_ptr<Mlt::Producer>> m_timelineProducers;
+
+    // the following holds a producer for each audio clip in the timeline
+    // keys are the id of the clips in the timeline, values are their values
+    std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_audioProducers;
+    std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_timewarpProducers;
+    std::shared_ptr<Mlt::Producer> m_videoProducer;
 
 signals:
     void producerChanged(const QString &, const std::shared_ptr<Mlt::Producer> &);
diff --git a/src/definitions.cpp b/src/definitions.cpp
index 1736abb30..bf55e8768 100644
--- a/src/definitions.cpp
+++ b/src/definitions.cpp
@@ -158,3 +158,19 @@ GroupType groupTypeFromStr(const QString &s)
     Q_ASSERT(false);
     return GroupType::Normal;
 }
+
+std::pair<bool, bool> stateToBool(PlaylistState state)
+{
+    return {state == PlaylistState::VideoOnly, state == PlaylistState::AudioOnly};
+}
+PlaylistState stateFromBool(std::pair<bool, bool> av)
+{
+    assert(!av.first || !av.second);
+    if (av.first) {
+        return PlaylistState::VideoOnly;
+    } else if (av.second) {
+        return PlaylistState::AudioOnly;
+    } else {
+        return PlaylistState::Disabled;
+    }
+}
diff --git a/src/definitions.h b/src/definitions.h
index ea53f6777..7158b1c20 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -77,10 +77,12 @@ enum OperationType {
     ZoomTimeline
 };
 
-namespace PlaylistState {
+enum class PlaylistState { VideoOnly = 1, AudioOnly = 2, Disabled = 3 };
+Q_DECLARE_METATYPE(PlaylistState)
 
-enum ClipState { Original = 0, VideoOnly = 1, AudioOnly = 2, Disabled = 3 };
-}
+// returns a pair corresponding to (video, audio)
+std::pair<bool, bool> stateToBool(PlaylistState state);
+PlaylistState stateFromBool(std::pair<bool, bool> av);
 
 namespace TimelineMode {
 enum EditMode { NormalEdit = 0, OverwriteEdit = 1, InsertEdit = 2 };
diff --git a/src/effects/effectstack/model/effectstackmodel.cpp \
b/src/effects/effectstack/model/effectstackmodel.cpp index dc968ed0c..0e2f1ef06 \
                100644
--- a/src/effects/effectstack/model/effectstackmodel.cpp
+++ b/src/effects/effectstack/model/effectstackmodel.cpp
@@ -48,36 +48,6 @@ std::shared_ptr<EffectStackModel> \
EffectStackModel::construct(std::weak_ptr<Mlt:  return self;
 }
 
-void EffectStackModel::loadEffects()
-{
-    auto ptr = m_service.lock();
-    m_loadingExisting = true;
-    if (ptr) {
-        for (int i = 0; i < ptr->filter_count(); i++) {
-            if (ptr->filter(i)->get("kdenlive_id") == nullptr) {
-                // don't consider internal MLT stuff
-                continue;
-            }
-            QString effectId = ptr->filter(i)->get("kdenlive_id");
-            auto effect = EffectItemModel::construct(ptr->filter(i), \
                shared_from_this());
-            // effect->setParameters
-            Fun redo = addItem_lambda(effect, rootItem->getId());
-            redo();
-            if (effectId == QLatin1String("fadein") || effectId == \
                QLatin1String("fade_from_black")) {
-                fadeIns << effect->getId();
-            } else if (effectId == QLatin1String("fadeout") || effectId == \
                QLatin1String("fade_to_black")) {
-                fadeOuts << effect->getId();
-            }
-            connect(effect.get(), &AssetParameterModel::modelChanged, this, \
                &EffectStackModel::modelChanged);
-            connect(effect.get(), &AssetParameterModel::replugEffect, this, \
                &EffectStackModel::replugEffect, Qt::DirectConnection);
-        }
-    } else {
-        qDebug() << "// CANNOT LOCK CLIP SEEVCE";
-    }
-    this->modelChanged();
-    m_loadingExisting = false;
-}
-
 void EffectStackModel::resetService(std::weak_ptr<Mlt::Service> service)
 {
     m_service = std::move(service);
@@ -112,8 +82,8 @@ void \
EffectStackModel::removeEffect(std::shared_ptr<EffectItemModel> effect)  if (res) {
         int inFades = fadeIns.size();
         int outFades = fadeOuts.size();
-        fadeIns.removeAll(effect->getId());
-        fadeOuts.removeAll(effect->getId());
+        fadeIns.erase(effect->getId());
+        fadeOuts.erase(effect->getId());
         inFades = fadeIns.size() - inFades;
         outFades = fadeOuts.size() - outFades;
         QString effectName = \
EffectsRepository::get()->getName(effect->getAssetId()); @@ -129,11 +99,10 @@ void \
EffectStackModel::removeEffect(std::shared_ptr<EffectItemModel> effect)  emit \
dataChanged(ix, ix, QVector<int>());  }
             }
-            //TODO: only update if effect is fade or keyframe
+            // TODO: only update if effect is fade or keyframe
             if (inFades < 0) {
                 pCore->updateItemModel(m_ownerId, QStringLiteral("fadein"));
-            }
-            else if (outFades < 0) {
+            } else if (outFades < 0) {
                 pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
             }
             pCore->updateItemKeyframes(m_ownerId);
@@ -146,23 +115,30 @@ void \
EffectStackModel::removeEffect(std::shared_ptr<EffectItemModel> effect)  }
 }
 
-void EffectStackModel::copyEffect(std::shared_ptr<AbstractEffectItem> sourceItem)
+void EffectStackModel::copyEffect(std::shared_ptr<AbstractEffectItem> sourceItem, \
bool logUndo)  {
     if (sourceItem->childCount() > 0) {
         // TODO: group
         return;
     }
     std::shared_ptr<EffectItemModel> sourceEffect = \
                std::static_pointer_cast<EffectItemModel>(sourceItem);
-    QString effectId = sourceEffect->getAssetId();
+    const QString effectId = sourceEffect->getAssetId();
     auto effect = EffectItemModel::construct(effectId, shared_from_this());
     effect->setParameters(sourceEffect->getAllParameters());
+    effect->filter().set("in", sourceEffect->filter().get_int("in"));
+    effect->filter().set("out", sourceEffect->filter().get_int("out"));
     Fun undo = removeItem_lambda(effect->getId());
     // TODO the parent should probably not always be the root
     Fun redo = addItem_lambda(effect, rootItem->getId());
     connect(effect.get(), &AssetParameterModel::modelChanged, this, \
                &EffectStackModel::modelChanged);
     connect(effect.get(), &AssetParameterModel::replugEffect, this, \
&EffectStackModel::replugEffect, Qt::DirectConnection); +    if (effectId == \
QLatin1String("fadein") || effectId == QLatin1String("fade_from_black")) { +        \
fadeIns.insert(effect->getId()); +    } else if (effectId == QLatin1String("fadeout") \
|| effectId == QLatin1String("fade_to_black")) { +        \
fadeOuts.insert(effect->getId()); +    }
     bool res = redo();
-    if (res) {
+    if (res && logUndo) {
         QString effectName = EffectsRepository::get()->getName(effectId);
         PUSH_UNDO(undo, redo, i18n("copy effect %1", effectName));
     }
@@ -188,19 +164,18 @@ void EffectStackModel::appendEffect(const QString &effectId, \
bool makeCurrent)  int inFades = 0;
         int outFades = 0;
         if (effectId == QLatin1String("fadein") || effectId == \
                QLatin1String("fade_from_black")) {
-            fadeIns << effect->getId();
+            fadeIns.insert(effect->getId());
             inFades++;
         } else if (effectId == QLatin1String("fadeout") || effectId == \
                QLatin1String("fade_to_black")) {
-            fadeOuts << effect->getId();
-            outFades ++;
+            fadeOuts.insert(effect->getId());
+            outFades++;
         }
         QString effectName = EffectsRepository::get()->getName(effectId);
         Fun update = [this, inFades, outFades]() {
-            //TODO: only update if effect is fade or keyframe
+            // TODO: only update if effect is fade or keyframe
             if (inFades > 0) {
                 pCore->updateItemModel(m_ownerId, QStringLiteral("fadein"));
-            }
-            else if (outFades > 0) {
+            } else if (outFades > 0) {
                 pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
             }
             pCore->updateItemKeyframes(m_ownerId);
@@ -227,11 +202,11 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, \
int oldIn, int oldD  auto ptr = m_service.lock();
     int out = newIn + duration;
     for (int i = 0; i < rootItem->childCount(); ++i) {
-        if (fadeInDuration > 0 && \
fadeIns.contains(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId())) { \
+        if (fadeInDuration > 0 && \
                fadeIns.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) \
>                 0) {
             std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  int oldEffectIn = \
qMax(0, effect->filter().get_in());  int oldEffectOut = effect->filter().get_out();
-            qDebug()<<"--previous effect: "<<oldEffectIn<<"-"<<oldEffectOut;
+            qDebug() << "--previous effect: " << oldEffectIn << "-" << oldEffectOut;
             int effectDuration = qMin(effect->filter().get_length() - 1, duration);
             indexes << getIndexFromItem(effect);
             if (!adjustFromEnd && (oldIn != newIn || duration != oldDuration)) {
@@ -239,7 +214,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int \
                oldIn, int oldD
                 Fun operation = [this, effect, newIn, effectDuration, logUndo]() {
                     effect->setParameter(QStringLiteral("in"), newIn, false);
                     effect->setParameter(QStringLiteral("out"), newIn + \
                effectDuration, logUndo);
-                    qDebug()<<"--new effect: "<<newIn<<"-"<<newIn + effectDuration;
+                    qDebug() << "--new effect: " << newIn << "-" << newIn + \
effectDuration;  return true;
                 };
                 operation();
@@ -264,14 +239,14 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, \
int oldIn, int oldD  if (operation() && logUndo) {
                     Fun reverse = [this, effect, referenceEffectOut]() {
                         effect->setParameter(QStringLiteral("out"), \
                referenceEffectOut, true);
-                        effect->filter().set("_refout", (char*) nullptr);
+                        effect->filter().set("_refout", (char *)nullptr);
                         return true;
                     };
                     PUSH_LAMBDA(operation, redo);
                     PUSH_LAMBDA(reverse, undo);
                 }
             }
-        } else if (fadeOutDuration > 0 && \
fadeOuts.contains(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId())) { \
+        } else if (fadeOutDuration > 0 && \
fadeOuts.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0) \
                {
             std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  int effectDuration = \
qMin(fadeOutDuration, duration);  int newFadeIn = out - effectDuration;
@@ -291,7 +266,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int \
                oldIn, int oldD
                 Fun reverse = [this, effect, referenceEffectIn, oldOut]() {
                     effect->setParameter(QStringLiteral("in"), referenceEffectIn, \
                false);
                     effect->setParameter(QStringLiteral("out"), oldOut, true);
-                    effect->filter().set("_refin", (char*) nullptr);
+                    effect->filter().set("_refin", (char *)nullptr);
                     return true;
                 };
                 PUSH_LAMBDA(operation, redo);
@@ -322,7 +297,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool \
fromStart, bool audio  {
     if (fromStart) {
         // Fade in
-        if (fadeIns.isEmpty()) {
+        if (fadeIns.empty()) {
             if (audioFade) {
                 appendEffect(QStringLiteral("fadein"));
             }
@@ -336,9 +311,9 @@ bool EffectStackModel::adjustFadeLength(int duration, bool \
fromStart, bool audio  if (ptr) {
             in = ptr->get_int("in");
         }
-        qDebug()<<"//// SETTING CLIP FADIN: "<<duration;
+        qDebug() << "//// SETTING CLIP FADIN: " << duration;
         for (int i = 0; i < rootItem->childCount(); ++i) {
-            if (fadeIns.contains(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId())) \
{ +            if (fadeIns.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) \
>                 0) {
                 std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  \
                effect->filter().set("in", in);
                 duration = qMin(pCore->getItemDuration(m_ownerId), duration);
@@ -352,7 +327,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool \
fromStart, bool audio  }
     } else {
         // Fade out
-        if (fadeOuts.isEmpty()) {
+        if (fadeOuts.empty()) {
             if (audioFade) {
                 appendEffect(QStringLiteral("fadeout"));
             }
@@ -368,7 +343,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool \
fromStart, bool audio  int out = in + pCore->getItemDuration(m_ownerId);
         QList<QModelIndex> indexes;
         for (int i = 0; i < rootItem->childCount(); ++i) {
-            if (fadeOuts.contains(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId())) \
{ +            if (fadeOuts.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) \
>                 0) {
                 std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  \
                effect->filter().set("out", out);
                 duration = qMin(pCore->getItemDuration(m_ownerId), duration);
@@ -377,7 +352,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool \
fromStart, bool audio  }
         }
         if (!indexes.isEmpty()) {
-            qDebug()<<"// UPDATING DATA INDEXES 2!!!";
+            qDebug() << "// UPDATING DATA INDEXES 2!!!";
             emit dataChanged(indexes.first(), indexes.last(), QVector<int>());
             pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
         }
@@ -388,21 +363,21 @@ bool EffectStackModel::adjustFadeLength(int duration, bool \
fromStart, bool audio  int EffectStackModel::getFadePosition(bool fromStart)
 {
     if (fromStart) {
-        if (fadeIns.isEmpty()) {
+        if (fadeIns.empty()) {
             return 0;
         }
         for (int i = 0; i < rootItem->childCount(); ++i) {
-            if (fadeIns.first() == \
std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) { +            if \
(*(fadeIns.begin()) == \
                std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) {
                 std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  return \
effect->filter().get_length();  }
         }
     } else {
-        if (fadeOuts.isEmpty()) {
+        if (fadeOuts.empty()) {
             return 0;
         }
         for (int i = 0; i < rootItem->childCount(); ++i) {
-            if (fadeOuts.first() == \
std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) { +            if \
(*(fadeOuts.begin()) == \
                std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) {
                 std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  return \
effect->filter().get_length();  }
@@ -413,20 +388,19 @@ int EffectStackModel::getFadePosition(bool fromStart)
 
 bool EffectStackModel::removeFade(bool fromStart)
 {
-    QList<int> toRemove;
+    std::vector<int> toRemove;
     for (int i = 0; i < rootItem->childCount(); ++i) {
-        if ((fromStart && \
                fadeIns.contains(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId())) \
                ||
-            (!fromStart && \
fadeOuts.contains(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()))) \
                {
-            toRemove << i;
+        if ((fromStart && \
fadeIns.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0) \
|| +            (!fromStart && \
fadeOuts.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0)) \
{ +            toRemove.push_back(i);
         }
     }
-    qSort(toRemove.begin(), toRemove.end(), qGreater<int>());
-    foreach (int i, toRemove) {
+    for (int i : toRemove) {
         std::shared_ptr<EffectItemModel> effect = \
std::static_pointer_cast<EffectItemModel>(rootItem->child(i));  if (fromStart) {
-            fadeIns.removeAll(rootItem->child(i)->getId());
+            fadeIns.erase(rootItem->child(i)->getId());
         } else {
-            fadeOuts.removeAll(rootItem->child(i)->getId());
+            fadeOuts.erase(rootItem->child(i)->getId());
         }
         removeEffect(effect);
     }
@@ -507,28 +481,27 @@ void \
                EffectStackModel::importEffects(std::shared_ptr<EffectStackModel> \
                sourceSta
     // TODO: manage fades, keyframes if clips don't have same size / in point
     for (int i = 0; i < sourceStack->rowCount(); i++) {
         auto item = sourceStack->getEffectStackRow(i);
-        if (item->childCount() > 0) {
-            // TODO: group
-            continue;
-        }
-        std::shared_ptr<EffectItemModel> effect = \
                std::static_pointer_cast<EffectItemModel>(item);
-        auto clone = EffectItemModel::construct(effect->getAssetId(), \
                shared_from_this());
-        rootItem->appendChild(clone);
-        clone->setParameters(effect->getAllParameters());
-        clone->filter().set("in", effect->filter().get_int("in"));
-        clone->filter().set("out", effect->filter().get_int("out"));
-        const QString effectId = effect->getAssetId();
-        if (effectId == QLatin1String("fadein") || effectId == \
                QLatin1String("fade_from_black")) {
-            fadeIns << clone->getId();
-        } else if (effectId == QLatin1String("fadeout") || effectId == \
                QLatin1String("fade_to_black")) {
-            fadeOuts << clone->getId();
+        copyEffect(item, false);
+    }
+    modelChanged();
+}
+
+void EffectStackModel::importEffects(std::weak_ptr<Mlt::Service> service, bool \
alreadyExist) +{
+    m_loadingExisting = alreadyExist;
+    if (auto ptr = service.lock()) {
+        for (int i = 0; i < ptr->filter_count(); i++) {
+            if (ptr->filter(i)->get("kdenlive_id") == nullptr) {
+                // don't consider internal MLT stuff
+                continue;
+            }
+            QString effectId = ptr->filter(i)->get("kdenlive_id");
+            auto effect = EffectItemModel::construct(ptr->filter(i), \
shared_from_this()); +            copyEffect(effect, false);
         }
-        // TODO parent should not always be root
-        Fun redo = addItem_lambda(clone, rootItem->getId());
-        connect(effect.get(), &AssetParameterModel::modelChanged, this, \
                &EffectStackModel::modelChanged);
-        connect(effect.get(), &AssetParameterModel::replugEffect, this, \
                &EffectStackModel::replugEffect, Qt::DirectConnection);
-        redo();
     }
+    m_loadingExisting = false;
+    modelChanged();
 }
 
 void EffectStackModel::setActiveEffect(int ix)
@@ -683,19 +656,18 @@ void \
EffectStackModel::replugEffect(std::shared_ptr<AssetParameterModel> asset)  
 void EffectStackModel::cleanFadeEffects(bool outEffects, Fun &undo, Fun &redo)
 {
-    QList<int> toDelete = outEffects ? fadeOuts : fadeIns;
+    const auto &toDelete = outEffects ? fadeOuts : fadeIns;
     for (int id : toDelete) {
         auto effect = std::static_pointer_cast<EffectItemModel>(getItemById(id));
         Fun operation = removeItem_lambda(id);
         if (operation()) {
             Fun reverse = addItem_lambda(effect, rootItem->getId());
-            PUSH_LAMBDA(operation, redo);
-            PUSH_LAMBDA(reverse, undo);
+            UPDATE_UNDO_REDO(operation, reverse, undo, redo);
         }
     }
-    if (!toDelete.isEmpty()) {
+    if (!toDelete.empty()) {
         Fun update = [this]() {
-            //TODO: only update if effect is fade or keyframe
+            // TODO: only update if effect is fade or keyframe
             pCore->updateItemKeyframes(m_ownerId);
             return true;
         };
diff --git a/src/effects/effectstack/model/effectstackmodel.hpp \
b/src/effects/effectstack/model/effectstackmodel.hpp index 0e81ddaa9..8a4f215e0 \
                100644
--- a/src/effects/effectstack/model/effectstackmodel.hpp
+++ b/src/effects/effectstack/model/effectstackmodel.hpp
@@ -29,6 +29,7 @@
 #include <QReadWriteLock>
 #include <memory>
 #include <mlt++/Mlt.h>
+#include <unordered_set>
 
 /* @brief This class an effect stack as viewed by the back-end.
    It is responsible for planting and managing effects into the producer it holds a \
pointer to. @@ -51,7 +52,6 @@ public:
     */
     static std::shared_ptr<EffectStackModel> construct(std::weak_ptr<Mlt::Service> \
service, ObjectId ownerId, std::weak_ptr<DocUndoStack> undo_stack);  void \
                resetService(std::weak_ptr<Mlt::Service> service);
-    void loadEffects();
 
 protected:
     EffectStackModel(std::weak_ptr<Mlt::Service> service, ObjectId ownerId, \
std::weak_ptr<DocUndoStack> undo_stack); @@ -59,11 +59,18 @@ protected:
 public:
     /* @brief Add an effect at the bottom of the stack */
     void appendEffect(const QString &effectId, bool makeCurrent = false);
-    /* @brief Copy an existing effect and append it at the bottom of the stack */
-    void copyEffect(std::shared_ptr<AbstractEffectItem> sourceItem);
+    /* @brief Copy an existing effect and append it at the bottom of the stack
+       @param logUndo: if true, an undo/redo is created
+     */
+    void copyEffect(std::shared_ptr<AbstractEffectItem> sourceItem, bool logUndo = \
true);  /* @brief Import all effects from the given effect stack
      */
     void importEffects(std::shared_ptr<EffectStackModel> sourceStack);
+    /* @brief Import all effects attached to a given service
+       @param alreadyExist: if true, the effect should be already attached to the \
service owned by this effectstack (it means we are in the process of loading). +      \
In that case, we need to build the stack but not replant the effects +     */
+    void importEffects(std::weak_ptr<Mlt::Service> service, bool alreadyExist = \
false);  bool removeFade(bool fromStart);
 
     /* @brief This function change the global (timeline-wise) enabled state of the \
effects @@ -120,13 +127,13 @@ protected:
 
 private:
     mutable QReadWriteLock m_lock;
-    QList<int> fadeIns;
-    QList<int> fadeOuts;
+    std::unordered_set<int> fadeIns;
+    std::unordered_set<int> fadeOuts;
+
     /** @brief: When loading a project, we load filters/effects that are already \
                planted
      *          in the producer, so we shouldn't plant them again. Setting this \
                value to
      *          true will prevent planting in the producer */
     bool m_loadingExisting;
-
 private slots:
     /** @brief: Some effects do not support dynamic changes like sox, and need to be \
                unplugged / replugged on each param change
      */
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index d7b2584c7..dd5aef928 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1403,23 +1403,17 @@ void MainWindow::setupActions()
 
     QAction *audioOnly = new \
QAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Audio Only"), \
this);  addAction(QStringLiteral("clip_audio_only"), audioOnly);
-    audioOnly->setData(PlaylistState::AudioOnly);
+    audioOnly->setData(QVariant::fromValue(PlaylistState::AudioOnly));
     audioOnly->setCheckable(true);
 
     QAction *videoOnly = new \
QAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Video Only"), \
this);  addAction(QStringLiteral("clip_video_only"), videoOnly);
-    videoOnly->setData(PlaylistState::VideoOnly);
+    videoOnly->setData(QVariant::fromValue(PlaylistState::VideoOnly));
     videoOnly->setCheckable(true);
 
-    QAction *audioAndVideo = new \
QAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Audio and \
                Video"), this);
-    addAction(QStringLiteral("clip_audio_and_video"), audioAndVideo);
-    audioAndVideo->setData(PlaylistState::Original);
-    audioAndVideo->setCheckable(true);
-
     m_clipTypeGroup = new QActionGroup(this);
     m_clipTypeGroup->addAction(audioOnly);
     m_clipTypeGroup->addAction(videoOnly);
-    m_clipTypeGroup->addAction(audioAndVideo);
     connect(m_clipTypeGroup, &QActionGroup::triggered, this, \
&MainWindow::slotUpdateClipType);  m_clipTypeGroup->setEnabled(false);
 
diff --git a/src/mltcontroller/clipcontroller.cpp \
b/src/mltcontroller/clipcontroller.cpp index a08a08cd2..7f7a83992 100644
--- a/src/mltcontroller/clipcontroller.cpp
+++ b/src/mltcontroller/clipcontroller.cpp
@@ -57,7 +57,7 @@ ClipController::ClipController(const QString clipId, \
std::shared_ptr<Mlt::Produc  return;
     }
     if (m_masterProducer) {
-        checkAudio();
+        checkAudioVideo();
     }
     if (m_properties) {
         setProducerProperty(QStringLiteral("kdenlive:id"), m_controllerBinId);
@@ -111,7 +111,7 @@ void ClipController::addMasterProducer(const \
std::shared_ptr<Mlt::Producer> &pro  m_producerLock.unlock();
         qCDebug(KDENLIVE_LOG) << "// WARNING, USING INVALID PRODUCER";
     } else {
-        checkAudio();
+        checkAudioVideo();
         m_producerLock.unlock();
         QString proxy = m_properties->get("kdenlive:proxy");
         m_service = m_properties->get("mlt_service");
@@ -303,7 +303,7 @@ void ClipController::updateProducer(const \
                std::shared_ptr<Mlt::Producer> &produc
     passProperties.pass_list(*m_properties, getPassPropertiesList(m_usesProxy));
     delete m_properties;
     *m_masterProducer = producer.get();
-    checkAudio();
+    checkAudioVideo();
     m_properties = new Mlt::Properties(m_masterProducer->get_properties());
     // Pass properties from previous producer
     m_properties->pass_list(passProperties, getPassPropertiesList(m_usesProxy));
@@ -322,40 +322,6 @@ void ClipController::updateProducer(const \
                std::shared_ptr<Mlt::Producer> &produc
     qDebug() << "// replace finished: " << binId() << " : " << \
m_masterProducer->get("resource");  }
 
-Mlt::Producer *ClipController::getTrackProducer(const QString &trackName, \
                PlaylistState::ClipState clipState, double speed)
-{
-    // TODO Delete this
-    Q_UNUSED(speed);
-    Q_UNUSED(trackName);
-    Q_UNUSED(clipState);
-    return m_masterProducer.get();
-    /*
-    //TODO
-    Q_UNUSED(speed)
-
-    if (trackName.isEmpty()) {
-        return m_masterProducer;
-    }
-    if (m_clipType != AV && m_clipType != Audio && m_clipType != Playlist) {
-        // Only producers with audio need a different producer for each track (or we \
                have an audio crackle bug)
-        return new Mlt::Producer(m_masterProducer->parent());
-    }
-    QString clipWithTrackId = clipId();
-    clipWithTrackId.append(QLatin1Char('_') + trackName);
-
-    //TODO handle audio / video only producers and framebuffer
-    if (clipState == PlaylistState::AudioOnly) {
-        clipWithTrackId.append(QStringLiteral("_audio"));
-    } else if (clipState == PlaylistState::VideoOnly) {
-        clipWithTrackId.append(QStringLiteral("_video"));
-    }
-
-    Mlt::Producer *clone = m_binController->cloneProducer(*m_masterProducer);
-    clone->set("id", clipWithTrackId.toUtf8().constData());
-    //m_binController->replaceBinPlaylistClip(clipWithTrackId, clone->parent());
-    return clone;
-    */
-}
 
 const QString ClipController::getStringDuration()
 {
@@ -569,12 +535,27 @@ bool ClipController::hasAudio() const
 {
     return m_hasAudio;
 }
-void ClipController::checkAudio()
+void ClipController::checkAudioVideo()
 {
     m_masterProducer->seek(0);
     Mlt::Frame *frame = m_masterProducer->get_frame();
-    // test_audio returns 1 if there is NO audio (strange but at the time this code \
is written) +    // test_audio returns 1 if there is NO audio (strange but true at \
the time this code is written)  m_hasAudio = frame->get_int("test_audio") == 0;
+    m_hasVideo = frame->get_int("test_image") == 0;
+}
+bool ClipController::hasVideo() const
+{
+    return m_hasVideo;
+}
+PlaylistState ClipController::defaultState() const
+{
+    if (hasVideo()) {
+        return PlaylistState::VideoOnly;
+    }
+    if (hasAudio()) {
+        return PlaylistState::AudioOnly;
+    }
+    return PlaylistState::Disabled;
 }
 
 QPixmap ClipController::pixmap(int framePosition, int width, int height)
diff --git a/src/mltcontroller/clipcontroller.h b/src/mltcontroller/clipcontroller.h
index cbdf05dc1..1fde5ade5 100644
--- a/src/mltcontroller/clipcontroller.h
+++ b/src/mltcontroller/clipcontroller.h
@@ -145,11 +145,6 @@ public:
 
     /** @brief Holds index of currently selected master clip effect. */
     int selectedEffectIndex;
-    /** @brief Get a clone of master producer for a specific track. Retrieve it if \
                it already exists
-     *  in our list, otherwise we create it.
-     *  Deprecated, track logic should be handled in timeline/track.cpp */
-    Q_DECL_DEPRECATED Mlt::Producer *getTrackProducer(const QString &trackName, \
                PlaylistState::ClipState clipState = PlaylistState::Original,
-                                                      double speed = 1.0);
 
     /** @brief Sets the master producer for this clip when we build the controller \
                without master clip. */
     void addMasterProducer(const std::shared_ptr<Mlt::Producer> &producer);
@@ -173,6 +168,10 @@ public:
     bool hasEffects() const;
     /** @brief Returns true if the clip contains at least one audio stream */
     bool hasAudio() const;
+    /** @brief Returns true if the clip contains at least one video stream */
+    bool hasVideo() const;
+    /** @brief Returns the default state a clip should be in. If the clips contains \
both video and audio, this defaults to video */ +    PlaylistState defaultState() \
const;  /** @brief Returns info about clip audio */
     const std::unique_ptr<AudioStreamInfo> &audioInfo() const;
     /** @brief Returns true if audio thumbnails for this clip are cached */
@@ -204,8 +203,8 @@ protected:
     virtual void emitProducerChanged(const QString &, const \
std::shared_ptr<Mlt::Producer> &){};  virtual void connectEffectStack(){};
 
-    // This is the helper function that checks the clip for audio and stores the \
                result
-    void checkAudio();
+    // This is the helper function that checks if the clip has audio and video and \
stores the result +    void checkAudioVideo();
 
     std::shared_ptr<Mlt::Producer> m_masterProducer;
     Mlt::Properties *m_properties;
@@ -223,6 +222,7 @@ protected:
     std::shared_ptr<EffectStackModel> m_effectStack;
     std::shared_ptr<MarkerListModel> m_markerModel;
     bool m_hasAudio;
+    bool m_hasVideo;
 
 private:
     QMutex m_producerLock;
diff --git a/src/timeline2/model/builders/meltBuilder.cpp \
b/src/timeline2/model/builders/meltBuilder.cpp index b1f344bfc..17963f5bb 100644
--- a/src/timeline2/model/builders/meltBuilder.cpp
+++ b/src/timeline2/model/builders/meltBuilder.cpp
@@ -35,7 +35,8 @@
 #include <mlt++/MltProducer.h>
 #include <mlt++/MltTransition.h>
 
-bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, int \
tid, Mlt::Tractor &track, const std::unordered_map<QString, QString> \
&binIdCorresp,Fun &undo, Fun &redo); +bool constructTrackFromMelt(const \
std::shared_ptr<TimelineItemModel> &timeline, int tid, Mlt::Tractor &track, +         \
const std::unordered_map<QString, QString> &binIdCorresp, Fun &undo, Fun &redo);  \
bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, int \
                tid, Mlt::Playlist &track,
                             const std::unordered_map<QString, QString> \
&binIdCorresp, Fun &undo, Fun &redo);  
@@ -103,7 +104,7 @@ bool constructTimelineFromMelt(const \
std::shared_ptr<TimelineItemModel> &timelin  
     // Loading compositions
     QScopedPointer<Mlt::Service> service(tractor.producer());
-    QList <Mlt::Transition *> compositions;
+    QList<Mlt::Transition *> compositions;
     while ((service != nullptr) && service->is_valid()) {
         if (service->type() == transition_type) {
             Mlt::Transition t((mlt_transition)service->get_service());
@@ -124,16 +125,17 @@ bool constructTimelineFromMelt(const \
std::shared_ptr<TimelineItemModel> &timelin  if (!compositions.isEmpty()) {
         std::sort(compositions.begin(), compositions.end(), [](Mlt::Transition *a, \
Mlt::Transition *b) { return a->get_b_track() < b->get_b_track(); });  while \
                (!compositions.isEmpty()) {
-            QScopedPointer<Mlt::Transition>t(compositions.takeFirst());
+            QScopedPointer<Mlt::Transition> t(compositions.takeFirst());
             Mlt::Properties transProps(t->get_properties());
             QString id(t->get("kdenlive_id"));
             int compoId;
-            ok = timeline->requestCompositionInsertion(id, \
timeline->getTrackIndexFromPosition(t->get_b_track() - 1), t->get_a_track(), \
t->get_in(), t->get_length(), &transProps, compoId, undo, redo); +            ok = \
timeline->requestCompositionInsertion(id, \
timeline->getTrackIndexFromPosition(t->get_b_track() - 1), t->get_a_track(), \
t->get_in(), +                                                       t->get_length(), \
&transProps, compoId, undo, redo);  if (!ok) {
                 qDebug() << "ERROR : failed to insert composition in track " << \
t->get_b_track() << ", position" << t->get_in();  break;
             }
-            qDebug() << "Inserted composition in track " << t->get_b_track() << ", \
position" << t->get_in()<<"/"<< t->get_out(); +            qDebug() << "Inserted \
composition in track " << t->get_b_track() << ", position" << t->get_in() << "/" << \
t->get_out();  }
     }
 
@@ -149,7 +151,8 @@ bool constructTimelineFromMelt(const \
std::shared_ptr<TimelineItemModel> &timelin  return true;
 }
 
-bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, int \
tid, Mlt::Tractor &track, const std::unordered_map<QString, QString> \
&binIdCorresp,Fun &undo, Fun &redo) +bool constructTrackFromMelt(const \
std::shared_ptr<TimelineItemModel> &timeline, int tid, Mlt::Tractor &track, +         \
const std::unordered_map<QString, QString> &binIdCorresp, Fun &undo, Fun &redo)  {
     if (track.count() != 2) {
         // we expect a tractor with two tracks (a "fake" track)
@@ -186,6 +189,37 @@ bool constructTrackFromMelt(const \
std::shared_ptr<TimelineItemModel> &timeline,  return true;
 }
 
+namespace {
+
+// This function tries to recover the state of the producer (audio or video or both)
+PlaylistState inferState(std::shared_ptr<Mlt::Producer> prod)
+{
+    auto getProperty = [prod](const QString &name) {
+        if (prod->parent().is_valid()) {
+            return QString::fromUtf8(prod->parent().get(name.toUtf8().constData()));
+        }
+        return QString::fromUtf8(prod->get(name.toUtf8().constData()));
+    };
+    auto getIntProperty = [prod](const QString &name) {
+        if (prod->parent().is_valid()) {
+            return prod->parent().get_int(name.toUtf8().constData());
+        }
+        return prod->get_int(name.toUtf8().constData());
+    };
+    QString service = getProperty("mlt_service");
+    std::pair<bool, bool> VidAud{true, true};
+    VidAud.first = getIntProperty("set.test_image") == 0;
+    VidAud.second = getIntProperty("set.test_audio") == 0;
+    if (service.contains(QStringLiteral("avformat")) && \
getIntProperty(QStringLiteral("video_index")) == -1) { +        VidAud.first = false;
+    }
+    if (service.contains(QStringLiteral("avformat")) && \
getIntProperty(QStringLiteral("audio_index")) == -1) { +        VidAud.second = \
false; +    }
+    return stateFromBool(VidAud);
+}
+} // namespace
+
 bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, int \
                tid, Mlt::Playlist &track,
                             const std::unordered_map<QString, QString> \
&binIdCorresp, Fun &undo, Fun &redo)  {
@@ -198,7 +232,7 @@ bool constructTrackFromMelt(const \
std::shared_ptr<TimelineItemModel> &timeline,  switch (clip->type()) {
         case unknown_type:
         case producer_type: {
-            //qDebug() << "Looking for clip clip "<< \
clip->parent().get("kdenlive:id")<<" = "<<clip->parent().get("kdenlive:clipname"); +  \
// qDebug() << "Looking for clip clip "<< clip->parent().get("kdenlive:id")<<" = \
"<<clip->parent().get("kdenlive:clipname");  QString binId;
             if (clip->parent().get_int("_kdenlive_processed") == 1) {
                 // This is a bin clip, already processed no need to change id
@@ -216,7 +250,8 @@ bool constructTrackFromMelt(const \
std::shared_ptr<TimelineItemModel> &timeline,  }
             bool ok = false;
             if (pCore->bin()->getBinClip(binId)) {
-                int cid = ClipModel::construct(timeline, binId, clip);
+                PlaylistState st = inferState(clip);
+                int cid = ClipModel::construct(timeline, binId, clip, st);
                 ok = timeline->requestClipMove(cid, tid, position, true, false, \
undo, redo);  } else {
                 qDebug() << "// Cannot find bin clip: " << binId << " - " << \
                clip->get("id");
diff --git a/src/timeline2/model/clipmodel.cpp b/src/timeline2/model/clipmodel.cpp
index 473b20b9a..24fa750b1 100644
--- a/src/timeline2/model/clipmodel.cpp
+++ b/src/timeline2/model/clipmodel.cpp
@@ -52,19 +52,47 @@ ClipModel::ClipModel(std::shared_ptr<TimelineModel> parent, \
std::shared_ptr<Mlt:  }
 }
 
-int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString \
&binClipId, int id, PlaylistState::ClipState state) +int ClipModel::construct(const \
std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id, \
PlaylistState state)  {
+    id = (id == -1 ? TimelineModel::getNextId() : id);
     std::shared_ptr<ProjectClip> binClip = \
                pCore->projectItemModel()->getClipByBinID(binClipId);
-    std::shared_ptr<Mlt::Producer> cutProducer = binClip->timelineProducer(state);
-    return construct(parent, binClipId, cutProducer, id);
+
+    // We refine the state according to what the clip can actually produce
+    std::pair<bool, bool> videoAudio = stateToBool(state);
+    videoAudio.first = videoAudio.first && binClip->hasVideo();
+    videoAudio.second = videoAudio.second && binClip->hasAudio();
+    state = stateFromBool(videoAudio);
+
+    std::shared_ptr<Mlt::Producer> cutProducer = binClip->getTimelineProducer(id, \
state, 1.); +
+    std::shared_ptr<ClipModel> clip(new ClipModel(parent, cutProducer, binClipId, \
id)); +    clip->setClipState(state);
+    parent->registerClip(clip);
+    return id;
 }
 
-int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString \
&binClipId, std::shared_ptr<Mlt::Producer> producer, int id) +int \
ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString \
&binClipId, std::shared_ptr<Mlt::Producer> producer, PlaylistState state)  {
-    std::shared_ptr<ClipModel> clip(new ClipModel(parent, producer, binClipId, id));
-    id = clip->m_id;
+
+    // we hand the producer to the bin clip, and in return we get a cut to a good \
master producer +    // We might not be able to use directly the producer that we \
receive as an argument, because it cannot share the same master producer with any \
other +    // clipModel (due to a mlt limitation, see ProjectClip doc)
+
+    int id = (id == -1 ? TimelineModel::getNextId() : id);
+    std::shared_ptr<ProjectClip> binClip = \
pCore->projectItemModel()->getClipByBinID(binClipId); +
+    // We refine the state according to what the clip can actually produce
+    std::pair<bool, bool> videoAudio = stateToBool(state);
+    videoAudio.first = videoAudio.first && binClip->hasVideo();
+    videoAudio.second = videoAudio.second && binClip->hasAudio();
+    state = stateFromBool(videoAudio);
+
+    auto result = binClip->giveMasterAndGetTimelineProducer(id, producer, state);
+    std::shared_ptr<ClipModel> clip(new ClipModel(parent, result.first, binClipId, \
id)); +    clip->m_effectStack->importEffects(producer, result.second);
+    clip->setClipState(state);
     parent->registerClip(clip);
-    clip->m_effectStack->loadEffects();
+
     return id;
 }
 
@@ -84,13 +112,11 @@ void ClipModel::deregisterClipToBin()
     binClip->deregisterTimelineClip(m_id);
 }
 
-ClipModel::~ClipModel()
-{
-}
+ClipModel::~ClipModel() {}
 
 bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool \
logUndo)  {
-    qDebug()<<"++++++++++ PERFORMAING CLIP RESIZE====";
+    qDebug() << "++++++++++ PERFORMAING CLIP RESIZE====";
     QWriteLocker locker(&m_lock);
     // qDebug() << "RESIZE CLIP" << m_id << "target size=" << size << "right=" << \
right << "endless=" << m_endlessResize << "total length" <<  // \
m_producer->get_length() << "current length" << getPlaytime(); @@ -154,7 +180,7 @@ \
bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l  }
             return false;
         };
-        qDebug()<<"// ADJUSTING EFFECT LENGTH, LOGUNDO "<<logUndo<<", \
"<<old_in<<"/"<<inPoint<<", "<<m_producer->get_playtime(); +        qDebug() << "// \
ADJUSTING EFFECT LENGTH, LOGUNDO " << logUndo << ", " << old_in << "/" << inPoint << \
                ", " << m_producer->get_playtime();
         if (logUndo) adjustEffectLength(right, old_in, inPoint, oldDuration, \
m_producer->get_playtime(), reverse, operation, logUndo);  \
UPDATE_UNDO_REDO(operation, reverse, undo, redo);  return true;
@@ -237,6 +263,13 @@ bool ClipModel::importEffects(std::shared_ptr<EffectStackModel> \
stackModel)  return true;
 }
 
+bool ClipModel::importEffects(std::weak_ptr<Mlt::Service> service)
+{
+    QWriteLocker locker(&m_lock);
+    m_effectStack->importEffects(service);
+    return true;
+}
+
 bool ClipModel::removeFade(bool fromStart)
 {
     QWriteLocker locker(&m_lock);
@@ -253,45 +286,34 @@ bool ClipModel::adjustEffectLength(bool adjustFromEnd, int \
oldIn, int newIn, int  bool ClipModel::adjustEffectLength(const QString &effectName, \
int duration, int originalDuration, Fun &undo, Fun &redo)  {
     QWriteLocker locker(&m_lock);
-    qDebug()<<".... ADJUSTING FADE LENGTH: "<<duration<<" / "<<effectName;
+    qDebug() << ".... ADJUSTING FADE LENGTH: " << duration << " / " << effectName;
     Fun operation = [this, duration, effectName]() {
-        return m_effectStack->adjustFadeLength(duration, effectName == \
QLatin1String("fadein") || effectName == QLatin1String("fade_to_black"), hasAudio(), \
!isAudioOnly()); +        return m_effectStack->adjustFadeLength(duration, effectName \
== QLatin1String("fadein") || effectName == QLatin1String("fade_to_black"), \
audioEnabled(), +                                               !isAudioOnly());
     };
     if (operation() && originalDuration > 0) {
         Fun reverse = [this, originalDuration, effectName]() {
-            return m_effectStack->adjustFadeLength(originalDuration, effectName == \
QLatin1String("fadein") || effectName == QLatin1String("fade_to_black"), hasAudio(), \
!isAudioOnly()); +            return \
m_effectStack->adjustFadeLength(originalDuration, effectName == \
QLatin1String("fadein") || effectName == QLatin1String("fade_to_black"), +            \
audioEnabled(), !isAudioOnly());  };
         UPDATE_UNDO_REDO(operation, reverse, undo, redo);
     }
     return true;
 }
 
-bool ClipModel::hasAudio() const
+bool ClipModel::audioEnabled() const
 {
     READ_LOCK();
-    QString service = getProperty("mlt_service");
-    if (service == QLatin1String("xml")) {
-        // Playlist clip, assume audio
-        return true;
-    }
-    qDebug() << "checking audio:" << service
-             << ((service.contains(QStringLiteral("avformat")) || service == \
                QLatin1String("timewarp")) &&
-                 (getIntProperty(QStringLiteral("audio_index")) > -1));
-    if ((service.contains(QStringLiteral("avformat")) || service == \
                QLatin1String("timewarp")) && \
                (getIntProperty(QStringLiteral("audio_index")) > -1)) {
-        return true;
-    }
-    std::shared_ptr<ProjectClip> binClip = \
                pCore->projectItemModel()->getClipByBinID(m_binClipId);
-    return binClip->hasAudio();
+    return stateToBool(m_currentState).second;
 }
 
 bool ClipModel::isAudioOnly() const
 {
     READ_LOCK();
-    QString service = getProperty("mlt_service");
-    return service.contains(QStringLiteral("avformat")) && \
(getIntProperty(QStringLiteral("video_index")) == -1); +    return m_currentState == \
PlaylistState::AudioOnly;  }
 
-void ClipModel::refreshProducerFromBin(PlaylistState::ClipState state)
+void ClipModel::refreshProducerFromBin(PlaylistState state)
 {
     QWriteLocker locker(&m_lock);
     if (getProperty("mlt_service") == QLatin1String("timewarp")) {
@@ -313,10 +335,9 @@ void ClipModel::refreshProducerFromBin(PlaylistState::ClipState \
state)  int in = getIn();
     int out = getOut();
     std::shared_ptr<ProjectClip> binClip = \
                pCore->projectItemModel()->getClipByBinID(m_binClipId);
-    std::shared_ptr<Mlt::Producer> binProducer = binClip->timelineProducer(state, \
m_currentTrackId); +    std::shared_ptr<Mlt::Producer> binProducer = \
binClip->getTimelineProducer(m_id, state);  m_producer = std::move(binProducer);
     m_producer->set_in_and_out(in, out);
-    // m_producer.reset(binProducer->cut(in, out));
     // replant effect stack in updated service
     m_effectStack->resetService(m_producer);
     m_producer->set("kdenlive:id", \
binClip->AbstractProjectItem::clipId().toUtf8().constData()); @@ -324,6 +345,10 @@ \
void ClipModel::refreshProducerFromBin(PlaylistState::ClipState state)  \
m_endlessResize = !binClip->hasLimitedDuration();  }
 
+void ClipModel::refreshProducerFromBin()
+{
+    refreshProducerFromBin(m_currentState);
+}
 
 bool ClipModel::useTimewarpProducer(double speed, int extraSpace, Fun &undo, Fun \
&redo)  {
@@ -357,7 +382,7 @@ Fun ClipModel::useTimewarpProducer_lambda(double speed, int \
extraSpace)  warp_out = out;
     }
     in = warp_in / speed;
-    out = qMin((int) (warp_out / speed), extraSpace);
+    out = qMin((int)(warp_out / speed), extraSpace);
     std::shared_ptr<ProjectClip> binClip = \
                pCore->projectItemModel()->getClipByBinID(m_binClipId);
     std::shared_ptr<Mlt::Producer> originalProducer = binClip->originalProducer();
     bool limitedDuration = binClip->hasLimitedDuration();
@@ -442,29 +467,20 @@ void ClipModel::setShowKeyframes(bool show)
     service()->set("kdenlive:hide_keyframes", (int)!show);
 }
 
-bool ClipModel::setClipState(PlaylistState::ClipState state)
+bool ClipModel::setClipState(PlaylistState state)
 {
     QWriteLocker locker(&m_lock);
-    if (!getProperty("mlt_service").startsWith(QStringLiteral("avformat"))) {
-        return false;
-    }
-    std::shared_ptr<ProjectClip> binClip = \
                pCore->projectItemModel()->getClipByBinID(m_binClipId);
-    std::shared_ptr<Mlt::Producer> binProducer = binClip->timelineProducer(state, \
                m_currentTrackId);
-    int in = getIn();
-    int out = getOut();
-    m_producer = std::move(binProducer);
-    m_producer->set_in_and_out(in, out);
-    m_producer->set("kdenlive:id", m_binClipId.toUtf8().constData());
-    m_producer->set("_kdenlive_cid", m_id);
-
-    // replant effect stack in updated service
-    m_effectStack->resetService(m_producer);
+    std::pair<bool, bool> VidAud = stateToBool(state);
+    m_producer->parent().set("set.test_image", int(VidAud.first ? 0 : 1));
+    m_producer->parent().set("set.test_audio", int(VidAud.second ? 0 : 1));
     return true;
 }
 
-PlaylistState::ClipState ClipModel::clipState() const
+PlaylistState ClipModel::clipState() const
 {
     READ_LOCK();
+    return m_currentState;
+    /*
     if (service()->parent().get_int("audio_index") == -1) {
         if (service()->parent().get_int("video_index") == -1) {
             return PlaylistState::Disabled;
@@ -475,9 +491,10 @@ PlaylistState::ClipState ClipModel::clipState() const
         return PlaylistState::AudioOnly;
     }
     return PlaylistState::Original;
+    */
 }
 
-void ClipModel::passTimelineProperties(std::shared_ptr <ClipModel> other)
+void ClipModel::passTimelineProperties(std::shared_ptr<ClipModel> other)
 {
     READ_LOCK();
     Mlt::Properties source(m_producer->get_properties());
diff --git a/src/timeline2/model/clipmodel.hpp b/src/timeline2/model/clipmodel.hpp
index 7026e92fc..9c6b32ad8 100644
--- a/src/timeline2/model/clipmodel.hpp
+++ b/src/timeline2/model/clipmodel.hpp
@@ -57,16 +57,14 @@ public:
        @param binClip is the id of the bin clip associated
        @param id Requested id of the clip. Automatic if -1
     */
-    static int construct(const std::shared_ptr<TimelineModel> &parent, const QString \
                &binClipId, int id = -1,
-                         PlaylistState::ClipState state = PlaylistState::Original);
-    /* @brief Creates a clip from an instance in MLT's playlist,
+    static int construct(const std::shared_ptr<TimelineModel> &parent, const QString \
&binClipId, int id, PlaylistState state); +
+    /* @brief Creates a clip, which references itself to the parent timeline
        Returns the (unique) id of the created clip
-       @param parent is a pointer to the timeline
-       @param binClip is the id of the bin clip associated
-       @param producer is the producer to be inserted
-       @param id Requested id of the clip. Automatic if -1
+    This variants assumes a producer is already known, which should typically happen \
only at loading time. +    Note that there is no guarantee that this producer is \
                actually going to be used. It might be discarded.
     */
-    static int construct(const std::shared_ptr<TimelineModel> &parent, const QString \
&binClipId, std::shared_ptr<Mlt::Producer> producer, int id = -1); +    static int \
construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, \
std::shared_ptr<Mlt::Producer> producer, PlaylistState state);  
     /* @brief returns a property of the clip, or from it's parent if it's a cut
      */
@@ -76,10 +74,11 @@ public:
     QSize getFrameSize() const;
     Q_INVOKABLE bool showKeyframes() const;
     Q_INVOKABLE void setShowKeyframes(bool show);
+
     /** @brief Returns the timeline clip status (video / audio only) */
-    PlaylistState::ClipState clipState() const;
+    PlaylistState clipState() const;
     /** @brief Sets the timeline clip status (video / audio only) */
-    bool setClipState(PlaylistState::ClipState state);
+    bool setClipState(PlaylistState state);
 
     /* @brief returns the length of the item on the timeline
      */
@@ -96,7 +95,11 @@ public:
 
     bool addEffect(const QString &effectId);
     bool copyEffect(std::shared_ptr<EffectStackModel> stackModel, int rowId);
+    /* @brief Import effects from a different stackModel */
     bool importEffects(std::shared_ptr<EffectStackModel> stackModel);
+    /* @brief Import effects from a service that contains some (another clip?) */
+    bool importEffects(std::weak_ptr<Mlt::Service> service);
+
     bool removeFade(bool fromStart);
     /** @brief Adjust effects duration. Should be called after each resize / cut \
                operation */
     bool adjustEffectLength(bool adjustFromEnd, int oldIn, int newIn, int \
oldDuration, int duration, Fun &undo, Fun &redo, bool logUndo); @@ -132,7 +135,8 @@ \
protected:  void setTimelineEffectsEnabled(bool enabled);
 
     /* @brief This functions should be called when the producer of the binClip \
                changes, to allow refresh */
-    void refreshProducerFromBin(PlaylistState::ClipState state = \
PlaylistState::Original); +    void refreshProducerFromBin(PlaylistState state);
+    void refreshProducerFromBin();
     /* @brief This functions replaces the current producer with a slowmotion one */
     bool useTimewarpProducer(double speed, int extraSpace, Fun &undo, Fun &redo);
     Fun useTimewarpProducer_lambda(double speed, int extraSpace);
@@ -140,7 +144,7 @@ protected:
     /** @brief Returns the marker model associated with this clip */
     std::shared_ptr<MarkerListModel> getMarkerModel() const;
 
-    bool hasAudio() const;
+    bool audioEnabled() const;
     bool isAudioOnly() const;
     double getSpeed() const;
 
@@ -154,6 +158,8 @@ protected:
     bool m_endlessResize; // Whether this clip can be freely resized
 
     bool forceThumbReload; // Used to trigger a forced thumb reload, when producer \
changes +
+    PlaylistState m_currentState;
 };
 
 #endif
diff --git a/src/timeline2/model/timelinefunctions.cpp \
b/src/timeline2/model/timelinefunctions.cpp index 2b81beee9..2ba24fc94 100644
--- a/src/timeline2/model/timelinefunctions.cpp
+++ b/src/timeline2/model/timelinefunctions.cpp
@@ -31,7 +31,7 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #include <QDebug>
 #include <klocalizedstring.h>
 
-bool TimelineFunctions::copyClip(std::shared_ptr<TimelineItemModel> timeline, int \
clipId, int &newId, PlaylistState::ClipState state, Fun &undo, Fun &redo) +bool \
TimelineFunctions::copyClip(std::shared_ptr<TimelineItemModel> timeline, int clipId, \
int &newId, PlaylistState state, Fun &undo, Fun &redo)  {
     // Special case: slowmotion clips
     double clipSpeed = timeline->m_allClips[clipId]->getSpeed();
@@ -94,7 +94,7 @@ bool \
TimelineFunctions::processClipCut(std::shared_ptr<TimelineItemModel> timeli  if \
(start > position || (start + duration) < position) {  return false;
     }
-    PlaylistState::ClipState state = timeline->m_allClips[clipId]->clipState();
+    PlaylistState state = timeline->m_allClips[clipId]->clipState();
     bool res = copyClip(timeline, clipId, newId, state, undo, redo);
     res = res && timeline->requestItemResize(clipId, position - start, true, true, \
undo, redo);  int newDuration = timeline->getClipPlaytime(clipId);
@@ -344,7 +344,7 @@ bool \
TimelineFunctions::requestItemCopy(std::shared_ptr<TimelineItemModel> timel  for (int \
id : allIds) {  int newId = -1;
         if (timeline->isClip(id)) {
-            PlaylistState::ClipState state = timeline->m_allClips[id]->clipState();
+            PlaylistState state = timeline->m_allClips[id]->clipState();
             res = copyClip(timeline, id, newId, state, undo, redo);
             res = res && (newId != -1);
         }
@@ -396,9 +396,9 @@ void \
                TimelineFunctions::showCompositionKeyframes(std::shared_ptr<TimelineItemMod
                
     timeline->dataChanged(modelIndex, modelIndex, {TimelineModel::KeyframesRole});
 }
 
-bool TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timeline, \
int clipId, PlaylistState::ClipState status) +bool \
TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timeline, int \
clipId, PlaylistState status)  {
-    PlaylistState::ClipState oldState = timeline->m_allClips[clipId]->clipState();
+    PlaylistState oldState = timeline->m_allClips[clipId]->clipState();
     if (oldState == status) {
         return true;
     }
@@ -411,9 +411,9 @@ bool \
TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timel  return \
result;  }
 
-bool TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timeline, \
int clipId, PlaylistState::ClipState status, Fun &undo, Fun &redo) +bool \
TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timeline, int \
clipId, PlaylistState status, Fun &undo, Fun &redo)  {
-    PlaylistState::ClipState oldState = timeline->m_allClips[clipId]->clipState();
+    PlaylistState oldState = timeline->m_allClips[clipId]->clipState();
     if (oldState == status) {
         return true;
     }
@@ -463,7 +463,7 @@ bool \
TimelineFunctions::requestSplitAudio(std::shared_ptr<TimelineItemModel> tim  // Now \
clear selection so we don't mess with groups  pCore->clearSelection();
     for (int cid : clips) {
-        if (!timeline->getClipPtr(cid)->hasAudio() || \
timeline->getClipPtr(cid)->clipState() == PlaylistState::AudioOnly) { +        if \
(!timeline->getClipPtr(cid)->audioEnabled() || timeline->getClipPtr(cid)->clipState() \
== PlaylistState::AudioOnly) {  // clip without audio or audio only, skip
             continue;
         }
diff --git a/src/timeline2/model/timelinefunctions.hpp \
b/src/timeline2/model/timelinefunctions.hpp index 21fe11bd5..5efd9723b 100644
--- a/src/timeline2/model/timelinefunctions.hpp
+++ b/src/timeline2/model/timelinefunctions.hpp
@@ -50,7 +50,7 @@ struct TimelineFunctions
     static bool processClipCut(std::shared_ptr<TimelineItemModel> timeline, int \
clipId, int position, int &newId, Fun &undo, Fun &redo);  
     /* @brief Makes a perfect copy of a given clip, but do not insert it */
-    static bool copyClip(std::shared_ptr<TimelineItemModel> timeline, int clipId, \
int &newId, PlaylistState::ClipState state, Fun &undo, Fun &redo); +    static bool \
copyClip(std::shared_ptr<TimelineItemModel> timeline, int clipId, int &newId, \
PlaylistState state, Fun &undo, Fun &redo);  
     /* @brief Request the addition of multiple clips to the timeline
      * If the addition of any of the clips fails, the entire operation is undone.
@@ -80,10 +80,10 @@ struct TimelineFunctions
      * @param status: target status of the clip
      This function creates an undo object and returns true on success
      */
-    static bool changeClipState(std::shared_ptr<TimelineItemModel> timeline, int \
clipId, PlaylistState::ClipState status); +    static bool \
changeClipState(std::shared_ptr<TimelineItemModel> timeline, int clipId, \
PlaylistState status);  /* @brief Same function as above, but accumulates for \
                undo/redo
      */
-    static bool changeClipState(std::shared_ptr<TimelineItemModel> timeline, int \
clipId, PlaylistState::ClipState status, Fun &undo, Fun &redo); +    static bool \
changeClipState(std::shared_ptr<TimelineItemModel> timeline, int clipId, \
PlaylistState status, Fun &undo, Fun &redo);  
     static bool requestSplitAudio(std::shared_ptr<TimelineItemModel> timeline, int \
                clipId, int audioTarget);
     static void setCompositionATrack(std::shared_ptr<TimelineItemModel> timeline, \
                int cid, int aTrack);
diff --git a/src/timeline2/model/timelineitemmodel.cpp \
b/src/timeline2/model/timelineitemmodel.cpp index d395ac25e..6c8ccf0f3 100644
--- a/src/timeline2/model/timelineitemmodel.cpp
+++ b/src/timeline2/model/timelineitemmodel.cpp
@@ -256,7 +256,7 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int \
role) const  case AudioLevelsRole:
             return clip->getAudioWaveform();
         case HasAudio:
-            return clip->hasAudio();
+            return clip->audioEnabled();
         case IsAudioRole:
             return clip->isAudioOnly();
         case MarkersRole: {
@@ -266,7 +266,7 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int \
                role) const
             return QVariant::fromValue<KeyframeModel *>(clip->getKeyframeModel());
         }
         case StatusRole:
-            return clip->clipState();
+            return QVariant::fromValue(clip->clipState());
         case StartRole:
             return clip->getPosition();
         case DurationRole:
diff --git a/src/timeline2/model/timelinemodel.cpp \
b/src/timeline2/model/timelinemodel.cpp index 627318da6..833b6628f 100644
--- a/src/timeline2/model/timelinemodel.cpp
+++ b/src/timeline2/model/timelinemodel.cpp
@@ -553,7 +553,7 @@ bool TimelineModel::requestClipInsertion(const QString \
&binClipId, int trackId,  return result;
 }
 
-bool TimelineModel::requestClipCreation(const QString &binClipId, int &id, \
PlaylistState::ClipState state, Fun &undo, Fun &redo) +bool \
TimelineModel::requestClipCreation(const QString &binClipId, int &id, PlaylistState \
state, Fun &undo, Fun &redo)  {
     int clipId = TimelineModel::getNextId();
     id = clipId;
@@ -658,7 +658,8 @@ bool TimelineModel::requestClipInsertion(const QString \
&binClipId, int trackId,  }
         }
     } else {
-        res = requestClipCreation(binClipId, id, PlaylistState::Original, \
local_undo, local_redo); +        std::shared_ptr<ProjectClip> binClip = \
pCore->projectItemModel()->getClipByBinID(binClipId); +        res = \
                requestClipCreation(binClipId, id, binClip->defaultState(), \
                local_undo, local_redo);
         res = res && requestClipMove(id, trackId, position, refreshView, logUndo, \
local_undo, local_redo);  }
     if (!res) {
diff --git a/src/timeline2/model/timelinemodel.hpp \
b/src/timeline2/model/timelinemodel.hpp index 50c8bf5c1..b7c0bb5fb 100644
--- a/src/timeline2/model/timelinemodel.hpp
+++ b/src/timeline2/model/timelinemodel.hpp
@@ -317,7 +317,7 @@ public:
        @param id: return parameter for the id of the newly created clip.
        @param state: The desired clip state (original, audio/video only).
      */
-    bool requestClipCreation(const QString &binClipId, int &id, \
PlaylistState::ClipState state, Fun &undo, Fun &redo); +    bool \
requestClipCreation(const QString &binClipId, int &id, PlaylistState state, Fun \
&undo, Fun &redo);  
     /* @brief Deletes the given clip or composition from the timeline This
        action is undoable Returns true on success. If it fails, nothing is
diff --git a/src/timeline2/view/timelinecontroller.cpp \
b/src/timeline2/view/timelinecontroller.cpp index 52e470b06..d8886e84d 100644
--- a/src/timeline2/view/timelinecontroller.cpp
+++ b/src/timeline2/view/timelinecontroller.cpp
@@ -1464,9 +1464,9 @@ void TimelineController::showCompositionKeyframes(int clipId, \
bool value)  TimelineFunctions::showCompositionKeyframes(m_model, clipId, value);
 }
 
-void TimelineController::setClipStatus(int clipId, int status)
+void TimelineController::setClipStatus(int clipId, PlaylistState status)
 {
-    TimelineFunctions::changeClipState(m_model, clipId, \
(PlaylistState::ClipState)status); +    TimelineFunctions::changeClipState(m_model, \
clipId, status);  }
 
 void TimelineController::splitAudio(int clipId)
diff --git a/src/timeline2/view/timelinecontroller.h \
b/src/timeline2/view/timelinecontroller.h index 34955f474..c66e0e81d 100644
--- a/src/timeline2/view/timelinecontroller.h
+++ b/src/timeline2/view/timelinecontroller.h
@@ -252,7 +252,7 @@ public:
     Q_INVOKABLE void removeSpace(int trackId = -1, int frame = -1, bool \
affectAllTracks = false);  /* @brief Change a clip status (normal / audio only / \
                video only)
      */
-    Q_INVOKABLE void setClipStatus(int clipId, int status);
+    Q_INVOKABLE void setClipStatus(int clipId, PlaylistState status);
 
     Q_INVOKABLE void requestClipCut(int clipId, int position);
 
diff --git a/tests/groupstest.cpp b/tests/groupstest.cpp
index 126455a85..270d386f2 100644
--- a/tests/groupstest.cpp
+++ b/tests/groupstest.cpp
@@ -482,11 +482,11 @@ TEST_CASE("Undo/redo", "[GroupsModel]")
 
     std::vector<int> clips;
     for (int i = 0; i < 4; i++) {
-        clips.push_back(ClipModel::construct(timeline, binId));
+        clips.push_back(ClipModel::construct(timeline, binId, -1, \
PlaylistState::VideoOnly));  }
     std::vector<int> clips2;
     for (int i = 0; i < 4; i++) {
-        clips2.push_back(ClipModel::construct(timeline2, binId));
+        clips2.push_back(ClipModel::construct(timeline2, binId, -1, \
PlaylistState::VideoOnly));  }
     int tid1 = TrackModel::construct(timeline);
     int tid2 = TrackModel::construct(timeline);
diff --git a/tests/modeltest.cpp b/tests/modeltest.cpp
index 363de3cf6..fbc42e62c 100644
--- a/tests/modeltest.cpp
+++ b/tests/modeltest.cpp
@@ -143,15 +143,15 @@ TEST_CASE("Basic creation/deletion of a clip", "[ClipModel]")
     QString binId2 = createProducer(profile_model, "green", binModel);
 
     REQUIRE(timeline->getClipsCount() == 0);
-    int id1 = ClipModel::construct(timeline, binId);
+    int id1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
     REQUIRE(timeline->getClipsCount() == 1);
     REQUIRE(timeline->checkConsistency());
 
-    int id2 = ClipModel::construct(timeline, binId2);
+    int id2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
     REQUIRE(timeline->getClipsCount() == 2);
     REQUIRE(timeline->checkConsistency());
 
-    int id3 = ClipModel::construct(timeline, binId);
+    int id3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
     REQUIRE(timeline->getClipsCount() == 3);
     REQUIRE(timeline->checkConsistency());
 
@@ -203,13 +203,13 @@ TEST_CASE("Clip manipulation", "[ClipModel]")
     QString binId2 = createProducer(profile_model, "blue", binModel);
     QString binId3 = createProducer(profile_model, "green", binModel);
 
-    int cid1 = ClipModel::construct(timeline, binId);
+    int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
     int tid1 = TrackModel::construct(timeline);
     int tid2 = TrackModel::construct(timeline);
     int tid3 = TrackModel::construct(timeline);
-    int cid2 = ClipModel::construct(timeline, binId2);
-    int cid3 = ClipModel::construct(timeline, binId3);
-    int cid4 = ClipModel::construct(timeline, binId2);
+    int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
+    int cid3 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
+    int cid4 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
 
     Verify(Method(timMock, _resetView)).Exactly(3_Times);
     RESET(timMock);
@@ -763,7 +763,7 @@ TEST_CASE("Clip manipulation", "[ClipModel]")
     SECTION("Group move with non-consecutive track ids")
     {
         int tid5 = TrackModel::construct(timeline);
-        int cid6 = ClipModel::construct(timeline, binId);
+        int cid6 = ClipModel::construct(timeline, binId, -1, \
PlaylistState::VideoOnly);  int tid6 = TrackModel::construct(timeline);
         REQUIRE(tid5 + 1 != tid6);
 
@@ -785,7 +785,7 @@ TEST_CASE("Clip manipulation", "[ClipModel]")
 
     SECTION("Clip copy")
     {
-        int cid6 = ClipModel::construct(timeline, binId);
+        int cid6 = ClipModel::construct(timeline, binId, -1, \
PlaylistState::VideoOnly);  int l = timeline->getClipPlaytime(cid6);
         REQUIRE(timeline->requestItemResize(cid6, l - 3, true, true, -1) == l - 3);
         REQUIRE(timeline->requestItemResize(cid6, l - 7, false, true, -1) == l - 7);
@@ -794,7 +794,7 @@ TEST_CASE("Clip manipulation", "[ClipModel]")
 
         std::function<bool(void)> undo = []() { return true; };
         std::function<bool(void)> redo = []() { return true; };
-        REQUIRE(TimelineFunctions::copyClip(timeline, cid6, newId, \
PlaylistState::Original, undo, redo)); +        \
REQUIRE(TimelineFunctions::copyClip(timeline, cid6, newId, PlaylistState::VideoOnly, \
                undo, redo));
         REQUIRE(timeline->m_allClips[cid6]->binId() == \
timeline->m_allClips[newId]->binId());  // TODO check effects
     }
@@ -842,7 +842,7 @@ TEST_CASE("Check id unicity", "[ClipModel]")
             track_ids.push_back(tid);
             REQUIRE(timeline->getTracksCount() == track_ids.size());
         } else {
-            int cid = ClipModel::construct(timeline, binId);
+            int cid = ClipModel::construct(timeline, binId, -1, \
PlaylistState::VideoOnly);  REQUIRE(all_ids.count(cid) == 0);
             all_ids.insert(cid);
             REQUIRE(timeline->getClipsCount() == all_ids.size() - track_ids.size());
@@ -882,10 +882,10 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
     QString binId = createProducer(profile_model, "red", binModel);
     QString binId2 = createProducer(profile_model, "blue", binModel);
 
-    int cid1 = ClipModel::construct(timeline, binId);
+    int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
     int tid1 = TrackModel::construct(timeline);
     int tid2 = TrackModel::construct(timeline);
-    int cid2 = ClipModel::construct(timeline, binId2);
+    int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
 
     timeline->m_allClips[cid1]->m_endlessResize = false;
     timeline->m_allClips[cid2]->m_endlessResize = false;
@@ -900,7 +900,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
             int temp;
             Fun undo = []() { return true; };
             Fun redo = []() { return true; };
-            REQUIRE_FALSE(timeline->requestClipCreation("impossible bin id", temp, \
PlaylistState::Original, undo, redo)); +            \
REQUIRE_FALSE(timeline->requestClipCreation("impossible bin id", temp, \
PlaylistState::VideoOnly, undo, redo));  }
 
         auto state0 = [&]() {
@@ -914,7 +914,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
         {
             Fun undo = []() { return true; };
             Fun redo = []() { return true; };
-            REQUIRE(timeline->requestClipCreation(binId3, cid3, \
PlaylistState::Original, undo, redo)); +            \
REQUIRE(timeline->requestClipCreation(binId3, cid3, PlaylistState::VideoOnly, undo, \
redo));  pCore->pushUndo(undo, redo, QString());
         }
 
@@ -931,7 +931,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
         {
             Fun undo = []() { return true; };
             Fun redo = []() { return true; };
-            REQUIRE(timeline->requestClipCreation(binId4, cid4, \
PlaylistState::Original, undo, redo)); +            \
REQUIRE(timeline->requestClipCreation(binId4, cid4, PlaylistState::VideoOnly, undo, \
redo));  pCore->pushUndo(undo, redo, QString());
         }
 
@@ -1358,7 +1358,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
         {
             std::function<bool(void)> undo = []() { return true; };
             std::function<bool(void)> redo = []() { return true; };
-            REQUIRE(timeline->requestClipCreation(binId, cid6, \
PlaylistState::Original, undo, redo)); +            \
REQUIRE(timeline->requestClipCreation(binId, cid6, PlaylistState::VideoOnly, undo, \
redo));  pCore->pushUndo(undo, redo, QString());
         }
         int l = timeline->getClipPlaytime(cid6);
@@ -1463,10 +1463,10 @@ TEST_CASE("Snapping", "[Snapping]")
     QString binId2 = createProducer(profile_model, "blue", binModel);
 
     int tid1 = TrackModel::construct(timeline);
-    int cid1 = ClipModel::construct(timeline, binId);
+    int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
     int tid2 = TrackModel::construct(timeline);
-    int cid2 = ClipModel::construct(timeline, binId2);
-    int cid3 = ClipModel::construct(timeline, binId2);
+    int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
+    int cid3 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
 
     timeline->m_allClips[cid1]->m_endlessResize = false;
     timeline->m_allClips[cid2]->m_endlessResize = false;
@@ -1582,20 +1582,20 @@ TEST_CASE("Advanced trimming operations", "[Trimming]")
     QString binId2 = createProducer(profile_model, "blue", binModel);
     QString binId3 = createProducerWithSound(profile_model, binModel);
 
-    int cid1 = ClipModel::construct(timeline, binId);
+    int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
     int tid1 = TrackModel::construct(timeline);
     int tid2 = TrackModel::construct(timeline);
     int tid3 = TrackModel::construct(timeline);
-    int cid2 = ClipModel::construct(timeline, binId2);
-    int cid3 = ClipModel::construct(timeline, binId);
-    int cid4 = ClipModel::construct(timeline, binId);
-    int cid5 = ClipModel::construct(timeline, binId);
-    int cid6 = ClipModel::construct(timeline, binId);
-    int cid7 = ClipModel::construct(timeline, binId);
-
-    int audio1 = ClipModel::construct(timeline, binId3);
-    int audio2 = ClipModel::construct(timeline, binId3);
-    int audio3 = ClipModel::construct(timeline, binId3);
+    int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
+    int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
+    int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
+    int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
+    int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
+    int cid7 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
+
+    int audio1 = ClipModel::construct(timeline, binId3, -1, \
PlaylistState::VideoOnly); +    int audio2 = ClipModel::construct(timeline, binId3, \
-1, PlaylistState::VideoOnly); +    int audio3 = ClipModel::construct(timeline, \
binId3, -1, PlaylistState::VideoOnly);  
     timeline->m_allClips[cid1]->m_endlessResize = false;
     timeline->m_allClips[cid2]->m_endlessResize = false;
diff --git a/tests/regressions.cpp b/tests/regressions.cpp
index 1b755ab30..9d84262e6 100644
--- a/tests/regressions.cpp
+++ b/tests/regressions.cpp
@@ -31,7 +31,7 @@ TEST_CASE("Regression")
     undoStack->redo();
     undoStack->undo();
     QString binId0 = createProducer(reg_profile, "red", binModel);
-    int c = ClipModel::construct(timeline, binId0);
+    int c = ClipModel::construct(timeline, binId0, -1, PlaylistState::VideoOnly);
     timeline->m_allClips[c]->m_endlessResize = false;
     TrackModel::construct(timeline);
     REQUIRE(timeline->getTrackById(1)->checkConsistency());
@@ -144,7 +144,7 @@ TEST_CASE("Regression2")
     REQUIRE(timeline->getTrackById(4)->checkConsistency());
     {
         QString binId0 = createProducer(reg_profile, "red", binModel);
-        int c = ClipModel::construct(timeline, binId0);
+        int c = ClipModel::construct(timeline, binId0, -1, \
PlaylistState::VideoOnly);  timeline->m_allClips[c]->m_endlessResize = false;
     }
     REQUIRE(timeline->getTrackById(0)->checkConsistency());


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic