[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