Cleanup, use MLT::Link for avfilters

This commit is contained in:
Jean-Baptiste Mardelle
2025-06-15 07:47:06 +02:00
parent 153f1fc200
commit 9af9d98763
9 changed files with 223 additions and 71 deletions

View File

@@ -917,9 +917,9 @@ std::shared_ptr<Mlt::Producer> ProjectClip::getTimelineProducer(int trackId, int
if (qFuzzyCompare(speed, 1.0) && !timeremapInfo.enableRemap) { if (qFuzzyCompare(speed, 1.0) && !timeremapInfo.enableRemap) {
// we are requesting a normal speed producer // we are requesting a normal speed producer
bool byPassTrackProducer = false; bool byPassTrackProducer = false;
if (trackId == -1 && (state != PlaylistState::AudioOnly || audioStream == m_masterProducer->get_int("audio_index"))) { /*if (trackId == -1 && (state != PlaylistState::AudioOnly || audioStream == m_masterProducer->get_int("audio_index"))) {
byPassTrackProducer = true; byPassTrackProducer = true;
} }*/
int maxDuration = 0; int maxDuration = 0;
if (m_clipType == ClipType::Timeline && m_masterProducer->parent().property_exists("kdenlive:maxduration")) { if (m_clipType == ClipType::Timeline && m_masterProducer->parent().property_exists("kdenlive:maxduration")) {
int duration = m_masterProducer->parent().get_int("kdenlive:maxduration"); int duration = m_masterProducer->parent().get_int("kdenlive:maxduration");
@@ -947,6 +947,7 @@ std::shared_ptr<Mlt::Producer> ProjectClip::getTimelineProducer(int trackId, int
return prod; return prod;
} }
if (m_usedProducers.count(clipId) > 0) { if (m_usedProducers.count(clipId) > 0) {
qDebug() << ":::: REQUESTING PRODUCER FOR CLIP: " << clipId << " ALREADY EXISTS!!!";
int duration = m_masterProducer->time_to_frames(m_masterProducer->get("kdenlive:duration")); int duration = m_masterProducer->time_to_frames(m_masterProducer->get("kdenlive:duration"));
return std::shared_ptr<Mlt::Producer>(m_usedProducers[clipId]->cut(-1, duration > 0 ? duration - 1 : -1)); return std::shared_ptr<Mlt::Producer>(m_usedProducers[clipId]->cut(-1, duration > 0 ? duration - 1 : -1));
m_effectStack->removeService(m_usedProducers[clipId]); m_effectStack->removeService(m_usedProducers[clipId]);
@@ -965,22 +966,22 @@ std::shared_ptr<Mlt::Producer> ProjectClip::getTimelineProducer(int trackId, int
if (secondPlaylist) { if (secondPlaylist) {
trackId = -trackId; trackId = -trackId;
} }
std::shared_ptr<Mlt::Producer> master;
if (m_clipType == ClipType::Timeline) { if (m_clipType == ClipType::Timeline) {
std::shared_ptr<Mlt::Producer> prod(m_masterProducer->cut(0, maxDuration)); master.reset(m_masterProducer->cut(0, maxDuration));
m_usedProducers[clipId] = prod;
} else { } else {
m_usedProducers[clipId] = cloneProducer(true, true); master = cloneProducer(true, true);
} }
m_usedProducers[clipId]->set("set.test_audio", 0); master->set("set.test_audio", 0);
m_usedProducers[clipId]->set("set.test_image", 1); master->set("set.test_image", 1);
if (m_streamEffects.contains(audioStream)) { if (m_streamEffects.contains(audioStream)) {
QStringList effects = m_streamEffects.value(audioStream); QStringList effects = m_streamEffects.value(audioStream);
for (const QString &effect : std::as_const(effects)) { for (const QString &effect : std::as_const(effects)) {
Mlt::Filter filt(m_usedProducers[clipId]->get_profile(), effect.toUtf8().constData()); Mlt::Filter filt(master->get_profile(), effect.toUtf8().constData());
if (filt.is_valid()) { if (filt.is_valid()) {
// Add stream effect markup // Add stream effect markup
filt.set("kdenlive:stream", 1); filt.set("kdenlive:stream", 1);
m_usedProducers[clipId]->attach(filt); master->attach(filt);
} }
} }
} }
@@ -989,19 +990,20 @@ std::shared_ptr<Mlt::Producer> ProjectClip::getTimelineProducer(int trackId, int
if (newAudioStreamIndex > -1) { if (newAudioStreamIndex > -1) {
/** If the audioStreamIndex is not found, for example when replacing a clip with another one using different indexes, /** If the audioStreamIndex is not found, for example when replacing a clip with another one using different indexes,
default to first audio stream */ default to first audio stream */
m_usedProducers[clipId]->set("audio_index", audioStream); master->set("audio_index", audioStream);
} else { } else {
newAudioStreamIndex = 0; newAudioStreamIndex = 0;
} }
if (newAudioStreamIndex > audioStreamsCount() - 1) { if (newAudioStreamIndex > audioStreamsCount() - 1) {
newAudioStreamIndex = 0; newAudioStreamIndex = 0;
} }
m_usedProducers[clipId]->set("astream", newAudioStreamIndex); master->set("astream", newAudioStreamIndex);
} }
m_effectStack->addService(m_usedProducers[clipId]); m_effectStack->addService(master);
std::shared_ptr<Mlt::Producer> prod(m_usedProducers.at(clipId)->cut()); m_usedProducers[clipId] = master;
if (m_clipType == ClipType::Timeline && m_usedProducers.at(clipId)->parent().property_exists("kdenlive:maxduration")) { std::shared_ptr<Mlt::Producer> prod(master->cut());
int max = m_usedProducers.at(clipId)->parent().get_int("kdenlive:maxduration"); if (m_clipType == ClipType::Timeline && master->parent().property_exists("kdenlive:maxduration")) {
int max = master->parent().get_int("kdenlive:maxduration");
prod->set("kdenlive:maxduration", max); prod->set("kdenlive:maxduration", max);
prod->set("length", max); prod->set("length", max);
} }
@@ -1011,25 +1013,28 @@ std::shared_ptr<Mlt::Producer> ProjectClip::getTimelineProducer(int trackId, int
// we return the video producer // we return the video producer
// We need to get an video producer, if none exists // We need to get an video producer, if none exists
// second playlist producers use negative trackId // second playlist producers use negative trackId
qDebug() << ":::: REQUESTING NEW VIDEO PRODUICER FOR: " << clipId;
if (secondPlaylist) { if (secondPlaylist) {
trackId = -trackId; trackId = -trackId;
} }
std::shared_ptr<Mlt::Producer> prod;
if (m_clipType == ClipType::Timeline) { if (m_clipType == ClipType::Timeline) {
std::shared_ptr<Mlt::Producer> prod(m_masterProducer->cut(0, maxDuration)); prod.reset(m_masterProducer->cut(0, maxDuration));
m_usedProducers[clipId] = prod;
} else { } else {
m_usedProducers[clipId] = cloneProducer(true, true); prod = cloneProducer(true, true);
} }
if (m_masterProducer->property_exists("kdenlive:maxduration")) { if (m_masterProducer->property_exists("kdenlive:maxduration")) {
m_usedProducers[clipId]->set("kdenlive:maxduration", m_masterProducer->get_int("kdenlive:maxduration")); prod->set("kdenlive:maxduration", m_masterProducer->get_int("kdenlive:maxduration"));
} }
// Let audio enabled so that we can use audio visualization filters ? // Let audio enabled so that we can use audio visualization filters ?
m_usedProducers[clipId]->set("set.test_audio", 1); prod->set("set.test_audio", 1);
m_usedProducers[clipId]->set("set.test_image", 0); prod->set("set.test_image", 0);
m_effectStack->addService(m_usedProducers.at(clipId)); m_effectStack->addService(prod);
m_usedProducers[clipId] = prod;
int duration = m_masterProducer->time_to_frames(m_masterProducer->get("kdenlive:duration")); int duration = m_masterProducer->time_to_frames(m_masterProducer->get("kdenlive:duration"));
return std::shared_ptr<Mlt::Producer>(m_usedProducers[clipId]->cut(-1, duration > 0 ? duration - 1 : -1)); // return std::shared_ptr<Mlt::Producer>(m_usedProducers[clipId]->cut(-1, duration > 0 ? duration - 1 : -1));
return std::shared_ptr<Mlt::Producer>(prod->cut(-1, duration > 0 ? duration - 1 : -1));
} }
Q_ASSERT(state == PlaylistState::Disabled); Q_ASSERT(state == PlaylistState::Disabled);
createDisabledMasterProducer(); createDisabledMasterProducer();
@@ -1352,6 +1357,7 @@ std::shared_ptr<Mlt::Producer> ProjectClip::cloneProducer(bool removeEffects, bo
Q_UNUSED(timelineProducer); Q_UNUSED(timelineProducer);
QMutexLocker lk(&m_producerMutex); QMutexLocker lk(&m_producerMutex);
QReadLocker lock(&pCore->xmlMutex); QReadLocker lock(&pCore->xmlMutex);
Mlt::Consumer c(pCore->getProjectProfile(), "xml", "string"); Mlt::Consumer c(pCore->getProjectProfile(), "xml", "string");
Mlt::Service s(m_masterProducer->get_service()); Mlt::Service s(m_masterProducer->get_service());
m_masterProducer->lock(); m_masterProducer->lock();
@@ -1361,10 +1367,10 @@ std::shared_ptr<Mlt::Producer> ProjectClip::cloneProducer(bool removeEffects, bo
} }
c.connect(s); c.connect(s);
c.set("time_format", "frames"); c.set("time_format", "frames");
c.set("no_meta", 1); // c.set("no_meta", 1);
c.set("no_root", 1); c.set("no_root", 1);
c.set("no_profile", 1); c.set("no_profile", 1);
c.set("root", "/"); c.set("root", "");
c.set("store", "kdenlive"); c.set("store", "kdenlive");
c.run(); c.run();
if (ignore) { if (ignore) {
@@ -1426,6 +1432,26 @@ std::shared_ptr<Mlt::Producer> ProjectClip::cloneProducer(bool removeEffects, bo
delete filter; delete filter;
filter = prod->filter(ct); filter = prod->filter(ct);
} }
if (prod->type() == mlt_service_chain_type) {
Mlt::Chain fromChain(*prod.get());
ct = 0;
Mlt::Link *filter = fromChain.link(ct);
while (filter) {
qDebug() << "// EFFECT " << ct << " : " << filter->get("mlt_service");
QString ix = QString::fromLatin1(filter->get("kdenlive_id"));
if (!ix.isEmpty()) {
qDebug() << "/ + + DELETING";
if (fromChain.detach(*filter) == 0) {
} else {
ct++;
}
} else {
ct++;
}
delete filter;
filter = fromChain.link(ct);
}
}
} }
prod->set("id", nullptr); prod->set("id", nullptr);
return prod; return prod;

View File

@@ -216,11 +216,6 @@ public:
/** @brief Returns a list of all timeline clip ids for this bin clip */ /** @brief Returns a list of all timeline clip ids for this bin clip */
QList<int> timelineInstances(QUuid activeUuid = QUuid()) const; QList<int> timelineInstances(QUuid activeUuid = QUuid()) const;
QMap<QUuid, QList<int>> getAllTimelineInstances() const; QMap<QUuid, QList<int>> getAllTimelineInstances() const;
/** @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 trackId, int clipId, PlaylistState::ClipState st, int audioStream = -1, double speed = 1.0,
bool secondPlaylist = false, const TimeWarpInfo timeremapInfo = {});
/** @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 /** @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 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
@@ -369,7 +364,11 @@ public Q_SLOTS:
void setInvalid(); void setInvalid();
void setClipStatus(FileStatus::ClipStatus status) override; void setClipStatus(FileStatus::ClipStatus status) override;
/** @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 trackId, int clipId, PlaylistState::ClipState st, int audioStream = -1, double speed = 1.0,
bool secondPlaylist = false, const TimeWarpInfo timeremapInfo = {});
/** /**
* Imports effect from a given producer * Imports effect from a given producer
* @param producer Producer containing the effects * @param producer Producer containing the effects

View File

@@ -237,11 +237,14 @@ bool EffectsRepository::isPreferred(const QString &effectId) const
return m_preferred_list.contains(effectId); return m_preferred_list.contains(effectId);
} }
std::unique_ptr<Mlt::Filter> EffectsRepository::getEffect(const QString &effectId) const std::unique_ptr<Mlt::Properties> EffectsRepository::getEffect(const QString &effectId) const
{ {
Q_ASSERT(exists(effectId)); Q_ASSERT(exists(effectId));
QString service_name = m_assets.at(effectId).mltId; QString service_name = m_assets.at(effectId).mltId;
// We create the Mlt element from its name // We create the Mlt element from its name
if (effectId.startsWith(QStringLiteral("avfilter."))) {
return std::make_unique<Mlt::Link>(service_name.toLatin1().constData());
}
auto filter = std::make_unique<Mlt::Filter>(pCore->getProjectProfile(), service_name.toLatin1().constData(), nullptr); auto filter = std::make_unique<Mlt::Filter>(pCore->getProjectProfile(), service_name.toLatin1().constData(), nullptr);
return filter; return filter;
} }

View File

@@ -24,7 +24,7 @@ public:
static std::unique_ptr<EffectsRepository> &get(); static std::unique_ptr<EffectsRepository> &get();
/** @brief returns a fresh instance of the given effect */ /** @brief returns a fresh instance of the given effect */
std::unique_ptr<Mlt::Filter> getEffect(const QString &effectId) const; std::unique_ptr<Mlt::Properties> getEffect(const QString &effectId) const;
/** @brief returns true if an effect exists in MLT (bypasses the excludelist/metadata parsing) */ /** @brief returns true if an effect exists in MLT (bypasses the excludelist/metadata parsing) */
bool hasInternalEffect(const QString &effectId) const; bool hasInternalEffect(const QString &effectId) const;
QPair<QString, QString> reloadCustom(const QString &path); QPair<QString, QString> reloadCustom(const QString &path);

View File

@@ -96,7 +96,16 @@ std::shared_ptr<EffectItemModel> EffectItemModel::construct(std::unique_ptr<Mlt:
void EffectItemModel::plant(const std::weak_ptr<Mlt::Service> &service) void EffectItemModel::plant(const std::weak_ptr<Mlt::Service> &service)
{ {
if (auto ptr = service.lock()) { if (auto ptr = service.lock()) {
int ret = ptr->attach(filter()); int ret = 0;
if (isLink()) {
qDebug() << ":::: TRYING TO PLANT LINK...";
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.attach(getLink());
} else {
qDebug() << ":::: TRYING TO PLANT FILTER...";
ret = ptr->attach(getFilter());
}
qDebug() << ":::: TRYING TO PLANT RESULT: " << ret;
Q_ASSERT(ret == 0); Q_ASSERT(ret == 0);
} else { } else {
qDebug() << "Error : Cannot plant effect because parent service is not available anymore"; qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
@@ -120,7 +129,7 @@ void EffectItemModel::loadClone(const std::weak_ptr<Mlt::Service> &service)
int out = m_asset->get_int("out"); int out = m_asset->get_int("out");
int in = m_asset->get_int("in"); int in = m_asset->get_int("in");
if (out > in) { if (out > in) {
effect->filter().set_in_and_out(in, out); effect->getFilter().set_in_and_out(in, out);
} }
int childId = ptr->get_int("_childid"); int childId = ptr->get_int("_childid");
if (childId == 0) { if (childId == 0) {
@@ -164,7 +173,7 @@ void EffectItemModel::plantClone(const std::weak_ptr<Mlt::Service> &service, int
int out = m_asset->get_int("out"); int out = m_asset->get_int("out");
int in = m_asset->get_int("in"); int in = m_asset->get_int("in");
if (out > in) { if (out > in) {
effect->filter().set_in_and_out(in, out); effect->getFilter().set_in_and_out(in, out);
} }
int childId = ptr->get_int("_childid"); int childId = ptr->get_int("_childid");
if (childId == 0) { if (childId == 0) {
@@ -172,10 +181,16 @@ void EffectItemModel::plantClone(const std::weak_ptr<Mlt::Service> &service, int
ptr->set("_childid", childId); ptr->set("_childid", childId);
} }
if (out > in) { if (out > in) {
effect->filter().set_in_and_out(in, out); effect->getFilter().set_in_and_out(in, out);
} }
m_childEffects.insert(childId, effect); m_childEffects.insert(childId, effect);
int ret = ptr->attach(effect->filter()); int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.attach(effect->getLink());
} else {
ret = ptr->attach(effect->getFilter());
}
if (ret == 0 && target > -1) { if (ret == 0 && target > -1) {
ptr->move_filter(ptr->count() - 1, target); ptr->move_filter(ptr->count() - 1, target);
} }
@@ -190,7 +205,13 @@ void EffectItemModel::plantClone(const std::weak_ptr<Mlt::Service> &service, int
void EffectItemModel::unplant(const std::weak_ptr<Mlt::Service> &service) void EffectItemModel::unplant(const std::weak_ptr<Mlt::Service> &service)
{ {
if (auto ptr = service.lock()) { if (auto ptr = service.lock()) {
int ret = ptr->detach(filter()); int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.detach(getLink());
} else {
ret = ptr->detach(getFilter());
}
Q_ASSERT(ret == 0); Q_ASSERT(ret == 0);
} else { } else {
qDebug() << "Error : Cannot plant effect because parent service is not available anymore"; qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
@@ -204,7 +225,13 @@ void EffectItemModel::unplantClone(const std::weak_ptr<Mlt::Service> &service)
return; return;
} }
if (auto ptr = service.lock()) { if (auto ptr = service.lock()) {
int ret = ptr->detach(filter()); int ret = 0;
if (isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.detach(getLink());
} else {
ret = ptr->detach(getFilter());
}
Q_ASSERT(ret == 0); Q_ASSERT(ret == 0);
if (!ptr->property_exists("_childid")) { if (!ptr->property_exists("_childid")) {
return; return;
@@ -212,7 +239,12 @@ void EffectItemModel::unplantClone(const std::weak_ptr<Mlt::Service> &service)
int childId = ptr->get_int("_childid"); int childId = ptr->get_int("_childid");
auto effect = m_childEffects.take(childId); auto effect = m_childEffects.take(childId);
if (effect && effect->isValid()) { if (effect && effect->isValid()) {
ptr->detach(effect->filter()); if (effect->isLink()) {
Mlt::Chain fromChain(static_cast<Mlt::Producer *>(ptr.get())->parent());
ret = fromChain.detach(effect->getLink());
} else {
ret = ptr->detach(effect->getFilter());
}
effect.reset(); effect.reset();
} else { } else {
qDebug() << "TRYING TO REMOVE INVALID EFFECT!!!!!!!"; qDebug() << "TRYING TO REMOVE INVALID EFFECT!!!!!!!";
@@ -223,11 +255,26 @@ void EffectItemModel::unplantClone(const std::weak_ptr<Mlt::Service> &service)
} }
} }
Mlt::Filter &EffectItemModel::filter() const Mlt::Properties &EffectItemModel::filter() const
{
return *(m_asset.get());
}
Mlt::Filter &EffectItemModel::getFilter() const
{ {
return *static_cast<Mlt::Filter *>(m_asset.get()); return *static_cast<Mlt::Filter *>(m_asset.get());
} }
Mlt::Link &EffectItemModel::getLink() const
{
return *static_cast<Mlt::Link *>(m_asset.get());
}
bool EffectItemModel::isLink() const
{
return m_assetId.startsWith(QLatin1String("avfilter."));
}
bool EffectItemModel::isValid() const bool EffectItemModel::isValid() const
{ {
return m_asset && m_asset->is_valid(); return m_asset && m_asset->is_valid();

View File

@@ -9,6 +9,7 @@
#include "abstractmodel/treeitem.hpp" #include "abstractmodel/treeitem.hpp"
#include "assets/model/assetparametermodel.hpp" #include "assets/model/assetparametermodel.hpp"
#include <mlt++/MltFilter.h> #include <mlt++/MltFilter.h>
#include <mlt++/MltLink.h>
class EffectStackModel; class EffectStackModel;
/** @brief This represents an effect of the effectstack /** @brief This represents an effect of the effectstack
@@ -37,7 +38,10 @@ public:
void unplant(const std::weak_ptr<Mlt::Service> &service) override; void unplant(const std::weak_ptr<Mlt::Service> &service) override;
void unplantClone(const std::weak_ptr<Mlt::Service> &service) override; void unplantClone(const std::weak_ptr<Mlt::Service> &service) override;
Mlt::Filter &filter() const; Mlt::Properties &filter() const;
Mlt::Filter &getFilter() const;
Mlt::Link &getLink() const;
bool isLink() const;
void setEffectStackEnabled(bool enabled) override; void setEffectStackEnabled(bool enabled) override;
/** @brief Return true if the effect applies only to audio */ /** @brief Return true if the effect applies only to audio */

View File

@@ -333,7 +333,7 @@ QDomElement EffectStackModel::toXml(QDomDocument &document)
QStringList passProps{QStringLiteral("disable"), QStringLiteral("kdenlive:collapsed"), QStringLiteral("kdenlive:builtin"), QStringList passProps{QStringLiteral("disable"), QStringLiteral("kdenlive:collapsed"), QStringLiteral("kdenlive:builtin"),
QStringLiteral("kdenlive:hiddenbuiltin"), QStringLiteral("kdenlive:kfrhidden")}; QStringLiteral("kdenlive:hiddenbuiltin"), QStringLiteral("kdenlive:kfrhidden")};
for (const QString &param : passProps) { for (const QString &param : passProps) {
int paramVal = sourceEffect->filter().get_int(param.toUtf8().constData()); int paramVal = sourceEffect->getFilter().get_int(param.toUtf8().constData());
if (paramVal > 0) { if (paramVal > 0) {
Xml::setXmlProperty(sub, param, QString::number(paramVal)); Xml::setXmlProperty(sub, param, QString::number(paramVal));
} }
@@ -358,7 +358,7 @@ QDomElement EffectStackModel::rowToXml(int row, QDomDocument &document)
std::shared_ptr<EffectItemModel> sourceEffect = std::static_pointer_cast<EffectItemModel>(rootItem->child(row)); std::shared_ptr<EffectItemModel> sourceEffect = std::static_pointer_cast<EffectItemModel>(rootItem->child(row));
QDomElement sub = document.createElement(QStringLiteral("effect")); QDomElement sub = document.createElement(QStringLiteral("effect"));
sub.setAttribute(QStringLiteral("id"), sourceEffect->getAssetId()); sub.setAttribute(QStringLiteral("id"), sourceEffect->getAssetId());
int filterIn = sourceEffect->filter().get_int("in"); int filterIn = sourceEffect->getFilter().get_int("in");
int filterOut = sourceEffect->filter().get_int("out"); int filterOut = sourceEffect->filter().get_int("out");
if (filterOut > filterIn) { if (filterOut > filterIn) {
sub.setAttribute(QStringLiteral("in"), filterIn); sub.setAttribute(QStringLiteral("in"), filterIn);
@@ -500,7 +500,7 @@ bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &re
connect(effect.get(), &AssetParameterModel::showEffectZone, this, &EffectStackModel::updateEffectZones); connect(effect.get(), &AssetParameterModel::showEffectZone, this, &EffectStackModel::updateEffectZones);
if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) { if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
m_fadeIns.insert(effect->getId()); m_fadeIns.insert(effect->getId());
int duration = effect->filter().get_length() - 1; int duration = effect->getFilter().get_length() - 1;
effect->filter().set("in", currentIn); effect->filter().set("in", currentIn);
effect->filter().set("out", currentIn + duration); effect->filter().set("out", currentIn + duration);
if (effectId.startsWith(QLatin1String("fade_"))) { if (effectId.startsWith(QLatin1String("fade_"))) {
@@ -525,7 +525,7 @@ bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &re
} }
} else if (effectId.startsWith(QLatin1String("fadeout")) || effectId.startsWith(QLatin1String("fade_to_"))) { } else if (effectId.startsWith(QLatin1String("fadeout")) || effectId.startsWith(QLatin1String("fade_to_"))) {
m_fadeOuts.insert(effect->getId()); m_fadeOuts.insert(effect->getId());
int duration = effect->filter().get_length() - 1; int duration = effect->getFilter().get_length() - 1;
int filterOut = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1; int filterOut = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1;
effect->filter().set("in", filterOut - duration); effect->filter().set("in", filterOut - duration);
effect->filter().set("out", filterOut); effect->filter().set("out", filterOut);
@@ -647,14 +647,14 @@ bool EffectStackModel::copyEffectWithUndo(const std::shared_ptr<AbstractEffectIt
QVector<int> roles = {TimelineModel::EffectNamesRole, TimelineModel::EffectCountRole}; QVector<int> roles = {TimelineModel::EffectNamesRole, TimelineModel::EffectCountRole};
if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) { if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
m_fadeIns.insert(effect->getId()); m_fadeIns.insert(effect->getId());
int duration = effect->filter().get_length() - 1; int duration = effect->getFilter().get_length() - 1;
int in = pCore->getItemIn(m_ownerId); int in = pCore->getItemIn(m_ownerId);
effect->filter().set("in", in); effect->filter().set("in", in);
effect->filter().set("out", in + duration); effect->filter().set("out", in + duration);
roles << TimelineModel::FadeInRole; roles << TimelineModel::FadeInRole;
} else if (effectId.startsWith(QLatin1String("fadeout")) || effectId.startsWith(QLatin1String("fade_to_"))) { } else if (effectId.startsWith(QLatin1String("fadeout")) || effectId.startsWith(QLatin1String("fade_to_"))) {
m_fadeOuts.insert(effect->getId()); m_fadeOuts.insert(effect->getId());
int duration = effect->filter().get_length() - 1; int duration = effect->getFilter().get_length() - 1;
int out = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1; int out = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1;
effect->filter().set("in", out - duration); effect->filter().set("in", out - duration);
effect->filter().set("out", out); effect->filter().set("out", out);
@@ -760,7 +760,7 @@ std::pair<bool, bool> EffectStackModel::doAppendEffect(const QString &effectId,
int inFades = 0; int inFades = 0;
int outFades = 0; int outFades = 0;
if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) { if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
int duration = effect->filter().get_length() - 1; int duration = effect->getFilter().get_length() - 1;
int in = pCore->getItemIn(m_ownerId); int in = pCore->getItemIn(m_ownerId);
effect->filter().set("in", in); effect->filter().set("in", in);
effect->filter().set("out", in + duration); effect->filter().set("out", in + duration);
@@ -830,10 +830,10 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(leaf); std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(leaf);
if (fadeInDuration > 0 && m_fadeIns.count(leaf->getId()) > 0) { if (fadeInDuration > 0 && m_fadeIns.count(leaf->getId()) > 0) {
// Adjust fade in // Adjust fade in
int oldEffectIn = qMax(0, effect->filter().get_in()); int oldEffectIn = qMax(0, effect->getFilter().get_in());
int oldEffectOut = effect->filter().get_out(); int oldEffectOut = effect->getFilter().get_out();
qDebug() << "--previous effect: " << oldEffectIn << "-" << oldEffectOut; qDebug() << "--previous effect: " << oldEffectIn << "-" << oldEffectOut;
int effectDuration = qMin(effect->filter().get_length() - 1, duration); int effectDuration = qMin(effect->getFilter().get_length() - 1, duration);
if (!adjustFromEnd && (oldIn != newIn || duration != oldDuration)) { if (!adjustFromEnd && (oldIn != newIn || duration != oldDuration)) {
// Clip start was resized, adjust effect in / out // Clip start was resized, adjust effect in / out
Fun operation = [effect, newIn, effectDuration, logUndo]() { Fun operation = [effect, newIn, effectDuration, logUndo]() {
@@ -927,7 +927,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
qDebug() << "// NULL Keyframes---------"; qDebug() << "// NULL Keyframes---------";
} }
if (m_ownerId.type == KdenliveObjectType::TimelineTrack && !hasZone) { if (m_ownerId.type == KdenliveObjectType::TimelineTrack && !hasZone) {
int oldEffectOut = effect->filter().get_out(); int oldEffectOut = effect->getFilter().get_out();
Fun operation = [effect, out, logUndo]() { Fun operation = [effect, out, logUndo]() {
effect->setParameter(QStringLiteral("out"), out, logUndo); effect->setParameter(QStringLiteral("out"), out, logUndo);
return true; return true;
@@ -945,12 +945,12 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
PUSH_LAMBDA(reverse, undo); PUSH_LAMBDA(reverse, undo);
} }
} else if (m_ownerId.type == KdenliveObjectType::TimelineClip && effect->data(QModelIndex(), AssetParameterModel::RequiresInOut).toBool() == true) { } else if (m_ownerId.type == KdenliveObjectType::TimelineClip && effect->data(QModelIndex(), AssetParameterModel::RequiresInOut).toBool() == true) {
int oldEffectIn = qMax(0, effect->filter().get_in()); int oldEffectIn = qMax(0, effect->getFilter().get_in());
int oldEffectOut = effect->filter().get_out(); int oldEffectOut = effect->getFilter().get_out();
int newIn = pCore->getItemIn(m_ownerId); int newIn = pCore->getItemIn(m_ownerId);
int newOut = newIn + pCore->getItemDuration(m_ownerId) - 1; int newOut = newIn + pCore->getItemDuration(m_ownerId) - 1;
Fun operation = [effect, newIn, newOut]() { Fun operation = [effect, newIn, newOut]() {
effect->filter().set_in_and_out(newIn, newOut); effect->getFilter().set_in_and_out(newIn, newOut);
qDebug() << "--new effect: " << newIn << "-" << newOut; qDebug() << "--new effect: " << newIn << "-" << newOut;
return true; return true;
}; };
@@ -959,7 +959,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldD
return false; return false;
} }
Fun reverse = [effect, oldEffectIn, oldEffectOut]() { Fun reverse = [effect, oldEffectIn, oldEffectOut]() {
effect->filter().set_in_and_out(oldEffectIn, oldEffectOut); effect->getFilter().set_in_and_out(oldEffectIn, oldEffectOut);
return true; return true;
}; };
PUSH_LAMBDA(operation, redo); PUSH_LAMBDA(operation, redo);
@@ -994,7 +994,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audio
if (m_fadeIns.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0) { if (m_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)); std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
if (oldDuration == -1) { if (oldDuration == -1) {
oldDuration = effect->filter().get_length(); oldDuration = effect->getFilter().get_length();
} }
effect->filter().set("in", in); effect->filter().set("in", in);
duration = qMin(pCore->getItemDuration(m_ownerId), duration); duration = qMin(pCore->getItemDuration(m_ownerId), duration);
@@ -1058,7 +1058,7 @@ bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audio
if (m_fadeOuts.count(std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) > 0) { if (m_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)); std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
if (oldDuration == -1) { if (oldDuration == -1) {
oldDuration = effect->filter().get_length(); oldDuration = effect->getFilter().get_length();
} }
effect->filter().set("out", out); effect->filter().set("out", out);
duration = qMin(itemDuration, duration); duration = qMin(itemDuration, duration);
@@ -1191,7 +1191,7 @@ int EffectStackModel::getFadePosition(bool fromStart)
for (int i = 0; i < rootItem->childCount(); ++i) { for (int i = 0; i < rootItem->childCount(); ++i) {
if (*(m_fadeIns.begin()) == std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) { if (*(m_fadeIns.begin()) == std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) {
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i)); std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
return effect->filter().get_length() - 1; return effect->getFilter().get_length() - 1;
} }
} }
} else { } else {
@@ -1201,7 +1201,7 @@ int EffectStackModel::getFadePosition(bool fromStart)
for (int i = 0; i < rootItem->childCount(); ++i) { for (int i = 0; i < rootItem->childCount(); ++i) {
if (*(m_fadeOuts.begin()) == std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) { if (*(m_fadeOuts.begin()) == std::static_pointer_cast<TreeItem>(rootItem->child(i))->getId()) {
std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i)); std::shared_ptr<EffectItemModel> effect = std::static_pointer_cast<EffectItemModel>(rootItem->child(i));
return effect->filter().get_length() - 1; return effect->getFilter().get_length() - 1;
} }
} }
} }
@@ -1303,7 +1303,7 @@ void EffectStackModel::registerItem(const std::shared_ptr<TreeItem> &item)
if (effectItem->data(QModelIndex(), AssetParameterModel::RequiresInOut).toBool() == true) { if (effectItem->data(QModelIndex(), AssetParameterModel::RequiresInOut).toBool() == true) {
int in = pCore->getItemIn(m_ownerId); int in = pCore->getItemIn(m_ownerId);
int out = in + pCore->getItemDuration(m_ownerId) - 1; int out = in + pCore->getItemDuration(m_ownerId) - 1;
effectItem->filter().set_in_and_out(in, out); effectItem->getFilter().set_in_and_out(in, out);
} }
if (!m_loadingExisting) { if (!m_loadingExisting) {
effectItem->plant(m_masterService); effectItem->plant(m_masterService);
@@ -1468,6 +1468,77 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
int imported = 0; int imported = 0;
int builtin = 0; int builtin = 0;
if (auto ptr = service.lock()) { if (auto ptr = service.lock()) {
// Import links if this is a chain
std::shared_ptr<Mlt::Chain> chain = nullptr;
if (ptr->type() == mlt_service_chain_type) {
Mlt::Producer prod(*ptr.get());
chain.reset(new Mlt::Chain(prod));
} else if (ptr->type() == mlt_service_producer_type) {
Mlt::Producer prod = static_cast<Mlt::Producer *>(ptr.get())->parent();
if (prod.type() == mlt_service_chain_type) {
chain.reset(new Mlt::Chain(prod));
}
}
if (chain) {
int maxLink = chain->link_count();
for (int i = 0; i < maxLink; i++) {
std::unique_ptr<Mlt::Link> filter(chain->link(i));
if (!filter->property_exists("kdenlive_id")) {
// don't consider internal MLT stuff
continue;
}
effectEnabled = filter->get_int("disable") == 0;
bool forcedInOut = filter->get_int("kdenlive:force_in_out") == 1 && filter->get_int("out") > 0;
if (disabledStack && filter->get_int("kdenlive:bin-disabled") == 0) {
disabledStack = false;
}
const QString effectId = qstrdup(filter->get("kdenlive_id"));
if (m_ownerId.type == KdenliveObjectType::TimelineClip && EffectsRepository::get()->isUnique(effectId) && hasFilter(effectId)) {
pCore->displayMessage(i18n("Effect %1 cannot be added twice.", EffectsRepository::get()->getName(effectId)), ErrorMessage);
continue;
}
// The MLT filter already exists, use it directly to create the effect
int filterIn = filter->get_in();
int filterOut = filter->get_out();
std::shared_ptr<EffectItemModel> effect;
if (alreadyExist) {
// effect is already plugged in the service
effect = EffectItemModel::construct(std::move(filter), shared_from_this(), originalDecimalPoint);
} else {
// duplicate effect
std::unique_ptr<Mlt::Properties> asset = EffectsRepository::get()->getEffect(effectId);
asset->inherit(*(filter));
effect = EffectItemModel::construct(std::move(asset), shared_from_this(), originalDecimalPoint);
}
if (state == PlaylistState::VideoOnly) {
if (effect->isAudio()) {
// Don't import effect
continue;
}
} else if (state == PlaylistState::AudioOnly) {
if (!effect->isAudio()) {
// Don't import effect
continue;
}
}
imported++;
connect(effect.get(), &AssetParameterModel::modelChanged, this, &EffectStackModel::modelChanged);
connect(effect.get(), &AssetParameterModel::replugEffect, this, &EffectStackModel::replugEffect, Qt::DirectConnection);
connect(effect.get(), &AssetParameterModel::showEffectZone, this, &EffectStackModel::updateEffectZones);
Fun redo = addItem_lambda(effect, rootItem->getId());
int clipIn = ptr->get_int("in");
int clipOut = ptr->get_int("out");
if (clipOut <= clipIn) {
clipOut = ptr->get_int("length") - 1;
}
effect->prepareKeyframes(clipIn, clipOut);
if (redo()) {
if (forcedInOut) {
effect->getFilter().set_in_and_out(filterIn, filterOut);
}
}
}
}
int max = ptr->filter_count(); int max = ptr->filter_count();
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
std::unique_ptr<Mlt::Filter> filter(ptr->filter(i)); std::unique_ptr<Mlt::Filter> filter(ptr->filter(i));
@@ -1519,7 +1590,7 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
effect = EffectItemModel::construct(std::move(filter), shared_from_this(), originalDecimalPoint); effect = EffectItemModel::construct(std::move(filter), shared_from_this(), originalDecimalPoint);
} else { } else {
// duplicate effect // duplicate effect
std::unique_ptr<Mlt::Filter> asset = EffectsRepository::get()->getEffect(effectId); std::unique_ptr<Mlt::Properties> asset = EffectsRepository::get()->getEffect(effectId);
asset->inherit(*(filter)); asset->inherit(*(filter));
effect = EffectItemModel::construct(std::move(asset), shared_from_this(), originalDecimalPoint); effect = EffectItemModel::construct(std::move(asset), shared_from_this(), originalDecimalPoint);
} }
@@ -1547,12 +1618,12 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
effect->prepareKeyframes(clipIn, clipOut); effect->prepareKeyframes(clipIn, clipOut);
if (redo()) { if (redo()) {
if (forcedInOut) { if (forcedInOut) {
effect->filter().set_in_and_out(filterIn, filterOut); effect->getFilter().set_in_and_out(filterIn, filterOut);
} else if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) { } else if (effectId.startsWith(QLatin1String("fadein")) || effectId.startsWith(QLatin1String("fade_from_"))) {
m_fadeIns.insert(effect->getId()); m_fadeIns.insert(effect->getId());
if (effect->filter().get_int("in") != clipIn) { if (effect->filter().get_int("in") != clipIn) {
// Broken fade, fix // Broken fade, fix
int filterLength = effect->filter().get_length() - 1; int filterLength = effect->getFilter().get_length() - 1;
effect->filter().set("in", clipIn); effect->filter().set("in", clipIn);
effect->filter().set("out", clipIn + filterLength); effect->filter().set("out", clipIn + filterLength);
} }
@@ -1560,7 +1631,7 @@ void EffectStackModel::importEffects(const std::weak_ptr<Mlt::Service> &service,
m_fadeOuts.insert(effect->getId()); m_fadeOuts.insert(effect->getId());
if (effect->filter().get_int("out") != clipOut) { if (effect->filter().get_int("out") != clipOut) {
// Broken fade, fix // Broken fade, fix
int filterLength = effect->filter().get_length() - 1; int filterLength = effect->getFilter().get_length() - 1;
effect->filter().set("in", clipOut - filterLength); effect->filter().set("in", clipOut - filterLength);
effect->filter().set("out", clipOut); effect->filter().set("out", clipOut);
} }

View File

@@ -43,7 +43,7 @@ ClipStabilize::ClipStabilize(const std::vector<QString> &binIds, QString filterN
if (m_filtername == QLatin1String("vidstab")) { if (m_filtername == QLatin1String("vidstab")) {
m_view = std::make_unique<AssetParameterView>(this); m_view = std::make_unique<AssetParameterView>(this);
qDebug() << "// Fetching effect: " << m_filtername; qDebug() << "// Fetching effect: " << m_filtername;
std::unique_ptr<Mlt::Filter> asset = EffectsRepository::get()->getEffect(m_filtername); std::unique_ptr<Mlt::Properties> asset = EffectsRepository::get()->getEffect(m_filtername);
auto prop = std::make_unique<Mlt::Properties>(asset->get_properties()); auto prop = std::make_unique<Mlt::Properties>(asset->get_properties());
QDomElement xml = EffectsRepository::get()->getXml(m_filtername); QDomElement xml = EffectsRepository::get()->getXml(m_filtername);
m_assetModel.reset(new AssetParameterModel(std::move(prop), xml, m_filtername, ObjectId())); m_assetModel.reset(new AssetParameterModel(std::move(prop), xml, m_filtername, ObjectId()));

View File

@@ -80,7 +80,7 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
videoAudio.first = videoAudio.first && binClip->hasVideo(); videoAudio.first = videoAudio.first && binClip->hasVideo();
videoAudio.second = videoAudio.second && binClip->hasAudio(); videoAudio.second = videoAudio.second && binClip->hasAudio();
state = stateFromBool(videoAudio); state = stateFromBool(videoAudio);
qDebug() << "// GET TIMELINE PROD FOR STREAM: " << audioStream; qDebug() << "// GET TIMELINE PROD FOR STREAM: " << audioStream << ", CID: " << id;
std::shared_ptr<Mlt::Producer> cutProducer = binClip->getTimelineProducer(-1, id, state, audioStream, speed); std::shared_ptr<Mlt::Producer> cutProducer = binClip->getTimelineProducer(-1, id, state, audioStream, speed);
std::shared_ptr<ClipModel> clip(new ClipModel(parent, cutProducer, binClipId, id, state, speed)); std::shared_ptr<ClipModel> clip(new ClipModel(parent, cutProducer, binClipId, id, state, speed));
if (!qFuzzyCompare(speed, 1.)) { if (!qFuzzyCompare(speed, 1.)) {
@@ -854,6 +854,7 @@ void ClipModel::refreshProducerFromBin(int trackId, PlaylistState::ClipState sta
{ {
// We require that the producer is not in the track when we refresh the producer, because otherwise the modification will not be propagated. Remove the clip // We require that the producer is not in the track when we refresh the producer, because otherwise the modification will not be propagated. Remove the clip
// first, refresh, and then replant. // first, refresh, and then replant.
// TODO Link: links seem to still be available when trying to refresh producer
QWriteLocker locker(&m_lock); QWriteLocker locker(&m_lock);
int in = getIn(); int in = getIn();
int out = getOut(); int out = getOut();
@@ -1243,7 +1244,8 @@ void ClipModel::setCurrentTrackId(int tid, bool finalMove)
if (finalMove && m_lastTrackId != m_currentTrackId) { if (finalMove && m_lastTrackId != m_currentTrackId) {
if (tid != -1) { if (tid != -1) {
refreshProducerFromBin(m_currentTrackId); // TODO Link: check if this doesn't break anything
// refreshProducerFromBin(m_currentTrackId);
} }
m_lastTrackId = m_currentTrackId; m_lastTrackId = m_currentTrackId;
} }