Refactoring: start moving effect related functions out of renderer.cpp in a separate class

parent 2d58c532
......@@ -25,7 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mltcontroller/effectscontroller.h"
#include "lib/audio/audioStreamInfo.h"
#include "timeline/timeline.h"
#include "renderer.h"
#include "timeline/effectmanager.h"
#include <QUrl>
#include <QDebug>
......@@ -636,7 +636,8 @@ void ClipController::addEffect(const ProfileInfo &pInfo, QDomElement &xml)
kdenlive_ix++;
xml.setAttribute(QStringLiteral("kdenlive_ix"), kdenlive_ix);
EffectsParameterList params = EffectsController::getEffectArgs(pInfo, xml);
Render::addFilterToService(service, params, getPlaytime().frames(m_binController->fps()));
EffectManager effect(service);
effect.addEffect(params, getPlaytime().frames(m_binController->fps()));
m_binController->updateTrackProducer(clipId());
}
......@@ -644,7 +645,8 @@ void ClipController::removeEffect(int effectIndex, bool delayRefresh)
{
QMutexLocker lock(&m_effectMutex);
Mlt::Service service(m_masterProducer->parent());
Render::removeFilterFromService(service, effectIndex, true);
EffectManager effect(service);
effect.removeEffect(effectIndex, true);
if (!delayRefresh)
m_binController->updateTrackProducer(clipId());
}
......
......@@ -1253,475 +1253,6 @@ void Render::mltInsertSpace(QMap <int, int> trackClipStartList, QMap <int, int>
m_mltConsumer->set("refresh", 1);
}
bool Render::mltRemoveTrackEffect(int track, int index, bool updateIndex)
{
Mlt::Service service(m_mltProducer->parent().get_service());
bool success = false;
Mlt::Tractor tractor(service);
//TODO: memleak
Mlt::Producer trackProducer(tractor.track(track));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
Mlt::Service clipService(trackPlaylist.get_service());
service.lock();
int ct = 0;
Mlt::Filter *filter = clipService.filter(ct);
while (filter) {
if ((index == -1 && strcmp(filter->get("kdenlive_id"), "")) || filter->get_int("kdenlive_ix") == index) {
if (clipService.detach(*filter) == 0) {
delete filter;
success = true;
}
} else if (updateIndex) {
// Adjust the other effects index
if (filter->get_int("kdenlive_ix") > index) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
ct++;
} else ct++;
filter = clipService.filter(ct);
}
service.unlock();
refresh();
return success;
}
bool Render::mltRemoveEffect(int track, GenTime position, int index, bool updateIndex, bool doRefresh)
{
if (position < GenTime()) {
// Remove track effect
return mltRemoveTrackEffect(track, index, updateIndex);
}
Mlt::Service service(m_mltProducer->parent().get_service());
bool success = false;
Mlt::Tractor tractor(service);
//TODO: memleak
Mlt::Producer trackProducer(tractor.track(track));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
QScopedPointer<Mlt::Producer> clip(trackPlaylist.get_clip(clipIndex));
if (!clip) {
qDebug() << " / / / CANNOT FIND CLIP TO REMOVE EFFECT";
return false;
}
Mlt::Service clipService(clip->get_service());
success = removeFilterFromService(clipService, index, updateIndex);
int duration = clip->get_playtime();
if (doRefresh) {
// Check if clip is visible in monitor
int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
if (diff < 0 || diff > duration) doRefresh = false;
}
if (doRefresh) refresh();
return success;
}
//static
bool Render::removeFilterFromService(Mlt::Service service, int effectIndex, bool updateIndex)
{
service.lock();
bool success = false;
int ct = 0;
Mlt::Filter *filter = service.filter(ct);
while (filter) {
if ((effectIndex == -1 && strcmp(filter->get("kdenlive_id"), "")) || filter->get_int("kdenlive_ix") == effectIndex) {
if (service.detach(*filter) == 0) {
delete filter;
success = true;
}
} else if (updateIndex) {
// Adjust the other effects index
if (filter->get_int("kdenlive_ix") > effectIndex) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
ct++;
} else ct++;
filter = service.filter(ct);
}
service.unlock();
return success;
}
bool Render::mltAddTrackEffect(int track, EffectsParameterList params)
{
Mlt::Service service(m_mltProducer->parent().get_service());
Mlt::Tractor tractor(service);
QScopedPointer <Mlt::Producer> tk(tractor.track(track));
Mlt::Producer trackProducer(tk.data());
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
Mlt::Service trackService(trackProducer.get_service()); //trackPlaylist
return mltAddEffect(trackService, params, trackProducer.get_playtime() - 1, true);
}
bool Render::mltAddEffect(int track, GenTime position, EffectsParameterList params, bool doRefresh)
{
Mlt::Service service(m_mltProducer->parent().get_service());
Mlt::Tractor tractor(service);
QScopedPointer <Mlt::Producer> tk(tractor.track(track));
Mlt::Producer trackProducer(tk.data());
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
QScopedPointer<Mlt::Producer> clip(trackPlaylist.get_clip(clipIndex));
if (!clip) {
return false;
}
Mlt::Service clipService(clip->get_service());
int duration = clip->get_playtime();
if (doRefresh) {
// Check if clip is visible in monitor
int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
if (diff < 0 || diff > duration) doRefresh = false;
}
return mltAddEffect(clipService, params, duration, doRefresh);
}
bool Render::mltAddEffect(Mlt::Service service, EffectsParameterList params, int duration, bool doRefresh)
{
bool updateIndex = false;
const int filter_ix = params.paramValue(QStringLiteral("kdenlive_ix")).toInt();
int ct = 0;
service.lock();
Mlt::Filter *filter = service.filter(ct);
while (filter) {
if (filter->get_int("kdenlive_ix") == filter_ix) {
// A filter at that position already existed, so we will increase all indexes later
updateIndex = true;
break;
}
ct++;
filter = service.filter(ct);
}
if (params.paramValue(QStringLiteral("id")) == QLatin1String("speed")) {
// special case, speed effect is not really inserted, we just update the other effects index (kdenlive_ix)
ct = 0;
filter = service.filter(ct);
while (filter) {
if (filter->get_int("kdenlive_ix") >= filter_ix) {
if (updateIndex) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") + 1);
}
ct++;
filter = service.filter(ct);
}
service.unlock();
if (doRefresh) refresh();
return true;
}
// temporarily remove all effects after insert point
QList <Mlt::Filter *> filtersList;
ct = 0;
filter = service.filter(ct);
while (filter) {
if (filter->get_int("kdenlive_ix") >= filter_ix) {
filtersList.append(filter);
service.detach(*filter);
} else ct++;
filter = service.filter(ct);
}
bool success = addFilterToService(service, params, duration);
// re-add following filters
for (int i = 0; i < filtersList.count(); ++i) {
Mlt::Filter *filter = filtersList.at(i);
if (updateIndex)
filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") + 1);
service.attach(*filter);
}
qDeleteAll(filtersList);
service.unlock();
if (doRefresh) refresh();
return success;
}
// static
bool Render::addFilterToService(Mlt::Service service, EffectsParameterList params, int duration)
{
// create filter
QString tag = params.paramValue(QStringLiteral("tag"));
QLocale locale;
////qDebug() << " / / INSERTING EFFECT: " << tag << ", REGI: " << region;
QString kfr = params.paramValue(QStringLiteral("keyframes"));
if (!kfr.isEmpty()) {
QStringList keyFrames = kfr.split(';', QString::SkipEmptyParts);
char *starttag = qstrdup(params.paramValue(QStringLiteral("starttag"), QStringLiteral("start")).toUtf8().constData());
char *endtag = qstrdup(params.paramValue(QStringLiteral("endtag"), QStringLiteral("end")).toUtf8().constData());
////qDebug() << "// ADDING KEYFRAME TAGS: " << starttag << ", " << endtag;
//double max = params.paramValue("max").toDouble();
double min = params.paramValue(QStringLiteral("min")).toDouble();
double factor = params.paramValue(QStringLiteral("factor"), QStringLiteral("1")).toDouble();
double paramOffset = params.paramValue(QStringLiteral("offset"), QStringLiteral("0")).toDouble();
params.removeParam(QStringLiteral("starttag"));
params.removeParam(QStringLiteral("endtag"));
params.removeParam(QStringLiteral("keyframes"));
params.removeParam(QStringLiteral("min"));
params.removeParam(QStringLiteral("max"));
params.removeParam(QStringLiteral("factor"));
params.removeParam(QStringLiteral("offset"));
// Special case, only one keyframe, means we want a constant value
if (keyFrames.count() == 1) {
Mlt::Filter *filter = new Mlt::Filter(*service.profile(), qstrdup(tag.toUtf8().constData()));
if (filter && filter->is_valid()) {
filter->set("kdenlive_id", qstrdup(params.paramValue(QStringLiteral("id")).toUtf8().constData()));
int x1 = keyFrames.at(0).section('=', 0, 0).toInt();
double y1 = keyFrames.at(0).section('=', 1, 1).toDouble();
for (int j = 0; j < params.count(); ++j) {
filter->set(params.at(j).name().toUtf8().constData(), params.at(j).value().toUtf8().constData());
}
filter->set("in", x1);
////qDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
filter->set(starttag, locale.toString(((min + y1) - paramOffset) / factor).toUtf8().data());
service.attach(*filter);
delete filter;
} else {
delete[] starttag;
delete[] endtag;
//qDebug() << "filter is NULL";
service.unlock();
return false;
}
} else for (int i = 0; i < keyFrames.size() - 1; ++i) {
Mlt::Filter *filter = new Mlt::Filter(*service.profile(), qstrdup(tag.toUtf8().constData()));
if (filter && filter->is_valid()) {
filter->set("kdenlive_id", qstrdup(params.paramValue(QStringLiteral("id")).toUtf8().constData()));
int x1 = keyFrames.at(i).section('=', 0, 0).toInt();
double y1 = keyFrames.at(i).section('=', 1, 1).toDouble();
int x2 = keyFrames.at(i + 1).section('=', 0, 0).toInt();
double y2 = keyFrames.at(i + 1).section('=', 1, 1).toDouble();
if (x2 == -1) x2 = duration;
// non-overlapping sections
if (i > 0) {
y1 += (y2 - y1) / (x2 - x1);
++x1;
}
for (int j = 0; j < params.count(); ++j) {
filter->set(params.at(j).name().toUtf8().constData(), params.at(j).value().toUtf8().constData());
}
filter->set("in", x1);
filter->set("out", x2);
////qDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
filter->set(starttag, locale.toString(((min + y1) - paramOffset) / factor).toUtf8().data());
filter->set(endtag, locale.toString(((min + y2) - paramOffset) / factor).toUtf8().data());
service.attach(*filter);
delete filter;
} else {
delete[] starttag;
delete[] endtag;
//qDebug() << "filter is NULL";
service.unlock();
return false;
}
}
delete[] starttag;
delete[] endtag;
} else {
Mlt::Filter *filter;
QString prefix;
filter = new Mlt::Filter(*service.profile(), qstrdup(tag.toUtf8().constData()));
if (filter && filter->is_valid()) {
filter->set("kdenlive_id", qstrdup(params.paramValue(QStringLiteral("id")).toUtf8().constData()));
} else {
//qDebug() << "filter is NULL";
service.unlock();
return false;
}
params.removeParam(QStringLiteral("kdenlive_id"));
if (params.paramValue(QStringLiteral("kdenlive:sync_in_out")) == QLatin1String("1")) {
// This effect must sync in / out with parent clip
//params.removeParam(QStringLiteral("_sync_in_out"));
filter->set_in_and_out(service.get_int("in"), service.get_int("out"));
}
for (int j = 0; j < params.count(); ++j) {
filter->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
}
if (tag == QLatin1String("sox")) {
QString effectArgs = params.paramValue(QStringLiteral("id")).section('_', 1);
params.removeParam(QStringLiteral("id"));
params.removeParam(QStringLiteral("kdenlive_ix"));
params.removeParam(QStringLiteral("tag"));
params.removeParam(QStringLiteral("disable"));
params.removeParam(QStringLiteral("region"));
for (int j = 0; j < params.count(); ++j) {
effectArgs.append(' ' + params.at(j).value());
}
////qDebug() << "SOX EFFECTS: " << effectArgs.simplified();
filter->set("effect", effectArgs.simplified().toUtf8().constData());
}
// attach filter to the clip
service.attach(*filter);
delete filter;
}
return true;
}
bool Render::mltEditTrackEffect(int track, EffectsParameterList params)
{
Mlt::Service service(m_mltProducer->parent().get_service());
Mlt::Tractor tractor(service);
//TODO: memleak
Mlt::Producer trackProducer(tractor.track(track));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
Mlt::Service clipService(trackPlaylist.get_service());
int ct = 0;
QString index = params.paramValue(QStringLiteral("kdenlive_ix"));
QString tag = params.paramValue(QStringLiteral("tag"));
Mlt::Filter *filter = clipService.filter(ct);
while (filter) {
if (filter->get_int("kdenlive_ix") == index.toInt()) {
break;
}
delete filter;
ct++;
filter = clipService.filter(ct);
}
if (!filter) {
//qDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT! " << index << ", " << tag;
// filter was not found, it was probably a disabled filter, so add it to the correct place...
bool success = false;//mltAddTrackEffect(track, params);
return success;
}
QString prefix;
QString ser = filter->get("mlt_service");
if (ser == QLatin1String("region")) prefix = QStringLiteral("filter0.");
service.lock();
for (int j = 0; j < params.count(); ++j) {
filter->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
}
service.unlock();
refresh();
return true;
}
bool Render::mltEditEffect(int track, const GenTime &position, EffectsParameterList params, bool replaceEffect)
{
int index = params.paramValue(QStringLiteral("kdenlive_ix")).toInt();
QString tag = params.paramValue(QStringLiteral("tag"));
if (!params.paramValue(QStringLiteral("keyframes")).isEmpty() || replaceEffect || tag.startsWith(QLatin1String("ladspa")) || tag == QLatin1String("sox") || tag == QLatin1String("autotrack_rectangle")) {
// This is a keyframe effect, to edit it, we remove it and re-add it.
if (mltRemoveEffect(track, position, index, false)) {
if (position < GenTime())
return mltAddTrackEffect(track, params);
else
return mltAddEffect(track, position, params);
}
}
if (position < GenTime()) {
return mltEditTrackEffect(track, params);
}
// find filter
Mlt::Service service(m_mltProducer->parent().get_service());
Mlt::Tractor tractor(service);
//TODO: memleak
Mlt::Producer trackProducer(tractor.track(track));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
QScopedPointer<Mlt::Producer> clip(trackPlaylist.get_clip(clipIndex));
if (!clip) {
//qDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
return false;
}
int duration = clip->get_playtime();
bool needRefresh = true;
// Check if clip is visible in monitor
int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
if (diff < 0 || diff > duration)
needRefresh = false;
int ct = 0;
Mlt::Filter *filter = clip->filter(ct);
while (filter) {
if (filter->get_int("kdenlive_ix") == index) {
break;
}
delete filter;
ct++;
filter = clip->filter(ct);
}
if (!filter) {
qDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT! " << index << ", " << tag;
// filter was not found, it was probably a disabled filter, so add it to the correct place...
bool success = mltAddEffect(track, position, params);
return success;
}
ct = 0;
QString ser = filter->get("mlt_service");
QList <Mlt::Filter *> filtersList;
service.lock();
if (ser != tag) {
// Effect service changes, delete effect and re-add it
clip->detach(*filter);
delete filter;
// Delete all effects after deleted one
filter = clip->filter(ct);
while (filter) {
if (filter->get_int("kdenlive_ix") > index) {
filtersList.append(filter);
clip->detach(*filter);
}
else ct++;
filter = clip->filter(ct);
}
// re-add filter
addFilterToService(*clip, params, clip->get_playtime());
service.unlock();
if (needRefresh)
refresh();
return true;
}
if (params.hasParam(QStringLiteral("kdenlive:sync_in_out"))) {
if (params.paramValue(QStringLiteral("kdenlive:sync_in_out")) == QLatin1String("1")) {
// This effect must sync in / out with parent clip
//params.removeParam(QStringLiteral("sync_in_out"));
filter->set_in_and_out(clip->get_in(), clip->get_out());
} else {
// Reset in/out properties
filter->set("in", (char*)NULL);
filter->set("out", (char*)NULL);
}
}
for (int j = 0; j < params.count(); ++j) {
filter->set(params.at(j).name().toUtf8().constData(), params.at(j).value().toUtf8().constData());
}
for (int j = 0; j < filtersList.count(); ++j) {
clip->attach(*(filtersList.at(j)));
}
qDeleteAll(filtersList);
service.unlock();
if (needRefresh)
doRefresh();
return true;
}
bool Render::mltEnableEffects(int track, const GenTime &position, const QList <int> &effectIndexes, bool disable)
{
if (position < GenTime()) {
......@@ -2367,6 +1898,7 @@ void Render::doPreviewRender(int start, int end, QDir folder, QString id, QStrin
args << "in=" + QString::number(i * 200);
args << "out=" + QString::number(i * 200 + 199);
args << "-consumer" << "avformat:" + folder.absoluteFilePath(fileName);
args << "an=1";
int result = QProcess::execute(KdenliveSettings::rendererpath(), args);
if (result < 0) {
// Something is wrong, abort
......
......@@ -193,17 +193,6 @@ class Render: public AbstractRender
int mltGetSpaceLength(const GenTime &pos, int track, bool fromBlankStart);
bool mltResizeClipCrop(ItemInfo info, GenTime newCropStart);
/** @brief Deletes an effect from a clip in MLT's playlist. */
bool mltRemoveEffect(int track, GenTime position, int index, bool updateIndex, bool doRefresh = true);
static bool removeFilterFromService(Mlt::Service service, int effectIndex, bool updateIndex);
bool mltRemoveTrackEffect(int track, int index, bool updateIndex);
/** @brief Adds an effect to a clip in MLT's playlist. */
bool mltAddEffect(int track, GenTime position, EffectsParameterList params, bool doRefresh = true);
static bool addFilterToService(Mlt::Service service, EffectsParameterList params, int duration);
bool mltAddEffect(Mlt::Service service, EffectsParameterList params, int duration, bool doRefresh);
bool mltAddTrackEffect(int track, EffectsParameterList params);
/** @brief Enable / disable clip effects.
* @param track The track where the clip is
* @param position The start position of the clip
......@@ -216,10 +205,6 @@ class Render: public AbstractRender
* @param disable True if effects should be disabled, false otherwise */
bool mltEnableTrackEffects(int track, const QList<int> &effectIndexes, bool disable);
/** @brief Edits an effect parameters in MLT's playlist. */
bool mltEditEffect(int track, const GenTime &position, EffectsParameterList params, bool replaceEffect);
bool mltEditTrackEffect(int track, EffectsParameterList params);
/** @brief Updates the "kdenlive_ix" (index) value of an effect. */
void mltUpdateEffectPosition(int track, const GenTime &position, int oldPos, int newPos);
......
......@@ -3,6 +3,7 @@ set(kdenlive_SRCS
timeline/abstractclipitem.cpp
timeline/abstractgroupitem.cpp
timeline/clip.cpp
timeline/effectmanager.cpp
timeline/clipdurationdialog.cpp
timeline/clipitem.cpp
timeline/customruler.cpp
......
......@@ -2026,13 +2026,13 @@ void CustomTrackView::slotRefreshEffects(ClipItem *clip)
{
int track = clip->track();
GenTime pos = clip->startPos();
if (!m_document->renderer()->mltRemoveEffect(track, pos, -1, false, false)) {
if (!m_timeline->track(track)->removeEffect(pos.seconds(), -1, false)) {
emit displayMessage(i18n("Problem deleting effect"), ErrorMessage);
return;
}
bool success = true;
for (int i = 0; i < clip->effectsCount(); ++i) {
if (!m_document->renderer()->mltAddEffect(track, pos, EffectsController::getEffectArgs(m_document->getProfileInfo(), clip->effect(i)), false)) success = false;
if (!m_timeline->track(track)->addEffect(pos.seconds(), EffectsController::getEffectArgs(m_document->getProfileInfo(), clip->effect(i)))) success = false;
}
if (!success) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
m_document->renderer()->doRefresh();
......@@ -2048,7 +2048,6 @@ void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect)
}
clearSelection();
m_timeline->addTrackEffect(track, effect);
m_document->renderer()->mltAddTrackEffect(track, EffectsController::getEffectArgs(m_document->getProfileInfo(), effect));
emit updateTrackEffectState(track);
emit showTrackEffects(track, m_timeline->getTrackInfo(track));
return;
......@@ -2072,7 +2071,8 @@ void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect)
return;
}
EffectsParameterList params = clip->addEffect(m_document->getProfileInfo(), effect);
if (!m_document->renderer()->mltAddEffect(track, pos, params)) {
if (!m_timeline->track(track)->addEffect(pos.seconds(), params)) {
//if (!m_document->renderer()->mltAddEffect(track, pos, params)) {
emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage);
clip->deleteEffect(params.paramValue(QStringLiteral("kdenlive_ix")).toInt());
}
......@@ -2086,10 +2086,9 @@ void CustomTrackView::deleteEffect(int track, const GenTime &pos, const QDomElem
int index = effect.attribute(QStringLiteral("kdenlive_ix")).toInt();
if (pos < GenTime()) {
// Delete track effect
if (m_document->renderer()->mltRemoveTrackEffect(track, index, true)) {
m_timeline->removeTrackEffect(track, effect);