Allow filter job effects on tracks and master stack, fix crash

Related to #570
parent 74a16575
Pipeline #15977 passed with stage
in 13 minutes and 54 seconds
......@@ -6,7 +6,7 @@
<parameter type="double" name="program" max="-10" min="-50" default="-23.00" decimals="2" suffix="LUFS">
<name>Target Program Loudness</name>
</parameter>
<parameter type="filterjob" filtertag="loudness" filterparams="%params" consumer="xml" consumerparams="video_off=1 no_meta=1 all=1 terminate_on_pause=1">
<parameter type="filterjob" filtertag="loudness" filterparams="%params" consumer="null" consumerparams="video_off=1 no_meta=1 all=1 terminate_on_pause=1">
<name>Analyse</name>
<jobparam name="key">results</jobparam>
<jobparam name="finalfilter">loudness</jobparam>
......
......@@ -41,7 +41,7 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
//QString name = m_model->data(m_index, AssetParameterModel::NameRole).toString();
QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString();
setToolTip(comment);
setEnabled(m_model->getOwnerId().first != ObjectType::TimelineTrack);
//setEnabled(m_model->getOwnerId().first != ObjectType::TimelineTrack);
auto *layout = new QVBoxLayout(this);
QVariantList filterData = m_model->data(m_index, AssetParameterModel::FilterJobParamsRole).toList();
QStringList filterAddedParams = m_model->data(m_index, AssetParameterModel::FilterParamsRole).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
......@@ -102,6 +102,9 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
binId = pCore->getTimelineClipBinId(cid);
in = pCore->getItemIn(owner);
out = in + pCore->getItemDuration(owner);
} else if (owner.first == ObjectType::TimelineTrack || owner.first == ObjectType::Master) {
in = 0;
out = pCore->getItemDuration(owner);
}
std::unordered_map<QString, QVariant> fParams;
std::unordered_map<QString, QString> fData;
......@@ -118,7 +121,7 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
fParams.insert({fparam.section(QLatin1Char('='), 0, 0), fparam.section(QLatin1Char('='), 1)});
}
}
pCore->jobManager()->startJob<FilterClipJob>({binId}, -1, QString(), cid, m_model, assetId, in, out, assetId, fParams, fData);
pCore->jobManager()->startJob<FilterClipJob>({binId}, -1, QString(), owner, m_model, assetId, in, out, assetId, fParams, fData);
if (m_label) {
m_label->setVisible(false);
}
......
......@@ -879,3 +879,21 @@ void Core::updateProjectTags(QMap <QString, QString> tags)
i++;
}
}
std::unique_ptr<Mlt::Producer> Core::getMasterProducerInstance()
{
if (m_guiConstructed && m_mainWindow->getCurrentTimeline()) {
std::unique_ptr<Mlt::Producer> producer(m_mainWindow->getCurrentTimeline()->controller()->tractor()->cut(0, m_mainWindow->getCurrentTimeline()->controller()->duration() - 1));
return producer;
}
return nullptr;
}
std::unique_ptr<Mlt::Producer> Core::getTrackProducerInstance(int tid)
{
if (m_guiConstructed && m_mainWindow->getCurrentTimeline()) {
std::unique_ptr<Mlt::Producer> producer(new Mlt::Producer(m_mainWindow->getCurrentTimeline()->controller()->trackProducer(tid)));
return producer;
}
return nullptr;
}
......@@ -38,6 +38,7 @@ class ProjectManager;
namespace Mlt {
class Repository;
class Producer;
class Profile;
} // namespace Mlt
......@@ -212,6 +213,10 @@ public:
/** @brief Returns the consumer profile, that will be scaled
* according to preview settings. Should only be used on the consumer */
Mlt::Profile *getProjectProfile();
/** @brief Returns a copy of current timeline's master playlist */
std::unique_ptr<Mlt::Producer> getMasterProducerInstance();
/** @brief Returns a copy of a track's playlist */
std::unique_ptr<Mlt::Producer> getTrackProducerInstance(int tid);
private:
explicit Core();
......
......@@ -22,6 +22,7 @@
#include "assets/model/assetparametermodel.hpp"
#include "bin/projectclip.h"
#include "bin/projectitemmodel.h"
#include "profiles/profilemodel.hpp"
#include "core.h"
#include "kdenlivesettings.h"
#include "macros.hpp"
......@@ -30,15 +31,35 @@
#include <klocalizedstring.h>
FilterClipJob::FilterClipJob(const QString &binId, int cid, std::weak_ptr<AssetParameterModel> model, const QString &assetId, int in, int out, const QString &filterName, std::unordered_map<QString, QVariant> filterParams, std::unordered_map<QString, QString> filterData)
FilterClipJob::FilterClipJob(const QString &binId, const ObjectId &owner, std::weak_ptr<AssetParameterModel> model, const QString &assetId, int in, int out, const QString &filterName, std::unordered_map<QString, QVariant> filterParams, std::unordered_map<QString, QString> filterData)
: MeltJob(binId, FILTERCLIPJOB, false, in, out)
, m_model(model)
, m_filterName(filterName)
, m_timelineClipId(cid)
, m_assetId(assetId)
, m_filterParams(std::move(filterParams))
, m_filterData(std::move(filterData))
, m_owner(owner)
{
m_timelineClipId = -1;
if (owner.first == ObjectType::TimelineClip) {
m_timelineClipId = owner.second;
}
}
void FilterClipJob::configureProducer()
{
if (m_producer != nullptr) {
// producer already configured, abort
return;
}
// We are on master or track, configure producer accordingly
if (m_owner.first == ObjectType::Master) {
m_profile.reset(&pCore->getCurrentProfile()->profile());
m_producer = std::move(pCore->getMasterProducerInstance());
} else if (m_owner.first == ObjectType::TimelineTrack) {
m_profile.reset(&pCore->getCurrentProfile()->profile());
m_producer = std::move(pCore->getTrackProducerInstance(m_owner.second));
}
}
const QString FilterClipJob::getDescription() const
......@@ -83,7 +104,11 @@ bool FilterClipJob::commitResult(Fun &undo, Fun &redo)
return false;
}
m_resultConsumed = true;
m_producer->detach(*m_filter.get());
if (!m_successful) {
m_filter.reset();
m_producer.reset();
m_wholeProducer.reset();
return false;
}
QVector<QPair<QString, QVariant>> params;
......@@ -102,8 +127,10 @@ bool FilterClipJob::commitResult(Fun &undo, Fun &redo)
}
auto operation = [assetModel = m_model, filterParams = std::move(params)]() {
if (auto ptr = assetModel.lock()) {
qDebug()<<"===== SETTING FILTER PARAM: "<<filterParams;
ptr->setParameters(filterParams);
}
pCore->setDocumentModified();
return true;
};
auto reverse = [assetModel = m_model, keyName = key]() {
......@@ -112,9 +139,13 @@ bool FilterClipJob::commitResult(Fun &undo, Fun &redo)
if (auto ptr = assetModel.lock()) {
ptr->setParameters(fParams);
}
pCore->setDocumentModified();
return true;
};
bool ok = operation();
m_filter.reset();
m_producer.reset();
m_wholeProducer.reset();
if (ok) {
UPDATE_UNDO_REDO_NOLOCK(operation, reverse, undo, redo);
}
......
......@@ -33,7 +33,7 @@ class FilterClipJob : public MeltJob
Q_OBJECT
public:
FilterClipJob(const QString &binId, int cid, std::weak_ptr<AssetParameterModel> model, const QString &assetId, int in, int out, const QString &filterName, std::unordered_map<QString, QVariant> filterParams, std::unordered_map<QString, QString> filterData);
FilterClipJob(const QString &binId, const ObjectId &owner, std::weak_ptr<AssetParameterModel> model, const QString &assetId, int in, int out, const QString &filterName, std::unordered_map<QString, QVariant> filterParams, std::unordered_map<QString, QString> filterData);
const QString getDescription() const override;
/** @brief This is to be called after the job finished.
By design, the job should store the result of the computation but not share it with the rest of the code. This happens when we call commitResult */
......@@ -43,6 +43,9 @@ protected:
// @brief create and configure consumer
void configureConsumer() override;
// @brief create and configure producer
void configureProducer() override;
// @brief create and configure filter
void configureFilter() override;
......@@ -53,6 +56,7 @@ protected:
QString m_assetId;
std::unordered_map<QString, QVariant> m_filterParams;
std::unordered_map<QString, QString> m_filterData;
ObjectId m_owner;
};
#endif
......@@ -55,101 +55,65 @@ MeltJob::MeltJob(const QString &binId, JOBTYPE type, bool useProducerProfile, in
bool MeltJob::startJob()
{
auto binClip = pCore->projectItemModel()->getClipByBinID(m_clipId);
m_url = binClip->url();
if (m_url.isEmpty()) {
m_errorMessage.append(i18n("No producer for this clip."));
m_successful = false;
m_done = true;
return false;
}
/*
QString consumerName = m_consumerParams.value(QStringLiteral("consumer"));
// safety check, make sure we don't overwrite a source clip
if (!m_dest.isEmpty() && !m_dest.endsWith(QStringLiteral(".mlt"))) {
m_errorMessage.append(i18n("Invalid destination: %1.", consumerName));
setStatus(JobCrashed);
return;
}
int in = m_producerParams.value(QStringLiteral("in")).toInt();
if (in > 0 && !m_extra.contains(QStringLiteral("offset"))) {
m_extra.insert(QStringLiteral("offset"), QString::number(in));
}
int out = m_producerParams.value(QStringLiteral("out")).toInt();
QString filterName = m_filterParams.value(QStringLiteral("filter"));
// optional params
int startPos = -1;
int track = -1;
// used when triggering a job from an effect
if (m_extra.contains(QStringLiteral("clipStartPos"))) {
startPos = m_extra.value(QStringLiteral("clipStartPos")).toInt();
}
if (m_extra.contains(QStringLiteral("clipTrack"))) {
track = m_extra.value(QStringLiteral("clipTrack")).toInt();
}
if (!m_extra.contains(QStringLiteral("finalfilter"))) {
m_extra.insert(QStringLiteral("finalfilter"), filterName);
}
if (binClip) {
// Filter applied on a timeline or bin clip
m_url = binClip->url();
if (m_url.isEmpty()) {
m_errorMessage.append(i18n("No producer for this clip."));
m_successful = false;
m_done = true;
return false;
}
if (out != -1 && out <= in) {
m_errorMessage.append(i18n("Clip zone undefined (%1 - %2).", in, out));
setStatus(JobCrashed);
return;
}
*/
auto &projectProfile = pCore->getCurrentProfile();
// bool producerProfile = m_extra.contains(QStringLiteral("producer_profile"));
if (m_useProducerProfile) {
m_profile.reset(new Mlt::Profile());
m_profile->set_explicit(0);
} else {
m_profile.reset(&projectProfile->profile());
}
double fps = projectProfile->fps();
int fps_num = projectProfile->frame_rate_num();
int fps_den = projectProfile->frame_rate_den();
if (KdenliveSettings::gpu_accel()) {
m_producer = binClip->getClone();
Mlt::Filter converter(*m_profile.get(), "avcolor_space");
m_producer->attach(converter);
} else {
m_producer = std::make_unique<Mlt::Producer>(*m_profile.get(), m_url.toUtf8().constData());
}
if (m_producer && m_useProducerProfile) {
m_profile->from_producer(*m_producer.get());
m_profile->set_explicit(1);
configureProfile();
if (!qFuzzyCompare(m_profile->fps(), fps)) {
// Reload producer
// Force same fps as projec profile or the resulting .mlt will not load in our project
qDebug()<<"/// FORCING FRAME RATE TO: "<<fps_num<<"\n-------------------";
m_profile->set_frame_rate(fps_num, fps_den);
auto &projectProfile = pCore->getCurrentProfile();
if (m_useProducerProfile) {
m_profile.reset(new Mlt::Profile());
m_profile->set_explicit(0);
} else {
m_profile.reset(&projectProfile->profile());
}
double fps = projectProfile->fps();
int fps_num = projectProfile->frame_rate_num();
int fps_den = projectProfile->frame_rate_den();
if (KdenliveSettings::gpu_accel()) {
m_producer = binClip->getClone();
Mlt::Filter converter(*m_profile.get(), "avcolor_space");
m_producer->attach(converter);
} else {
m_producer = std::make_unique<Mlt::Producer>(*m_profile.get(), m_url.toUtf8().constData());
}
}
if ((m_producer == nullptr) || !m_producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
m_errorMessage.append(i18n("Invalid clip"));
m_successful = false;
m_done = true;
return false;
}
/*
// Process producer params
QMapIterator<QString, QString> i(m_producerParams);
QStringList ignoredProps;
ignoredProps << QStringLiteral("producer") << QStringLiteral("in") << QStringLiteral("out");
while (i.hasNext()) {
i.next();
QString key = i.key();
if (!ignoredProps.contains(key)) {
producer->set(i.key().toUtf8().constData(), i.value().toUtf8().constData());
if (m_producer && m_useProducerProfile) {
m_profile->from_producer(*m_producer.get());
m_profile->set_explicit(1);
configureProfile();
if (!qFuzzyCompare(m_profile->fps(), fps)) {
// Reload producer
// Force same fps as projec profile or the resulting .mlt will not load in our project
qDebug()<<"/// FORCING FRAME RATE TO: "<<fps_num<<"\n-------------------";
m_profile->set_frame_rate(fps_num, fps_den);
m_producer = std::make_unique<Mlt::Producer>(*m_profile.get(), m_url.toUtf8().constData());
}
}
if ((m_producer == nullptr) || !m_producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
m_errorMessage.append(i18n("Invalid clip"));
m_successful = false;
m_done = true;
return false;
}
if (m_out == -1) {
m_out = m_producer->get_length() - 1;
}
if (m_in == -1) {
m_in = 0;
}
if (m_out != m_producer->get_length() - 1 || m_in != 0) {
std::swap(m_wholeProducer, m_producer);
m_producer.reset(m_wholeProducer->cut(m_in, m_out));
}
} else {
// Filter applied on a track of master producer, leave config to source job
}
*/
configureProducer();
if ((m_producer == nullptr) || !m_producer->is_valid()) {
......@@ -159,16 +123,6 @@ bool MeltJob::startJob()
m_done = true;
return false;
}
if (m_out == -1) {
m_out = m_producer->get_length() - 1;
}
if (m_in == -1) {
m_in = 0;
}
if (m_out != m_producer->get_length() - 1 || m_in != 0) {
std::swap(m_wholeProducer, m_producer);
m_producer.reset(m_wholeProducer->cut(m_in, m_out));
}
// Build consumer
configureConsumer();
......@@ -252,6 +206,7 @@ bool MeltJob::startJob()
length = m_producer->get_length();
}
if (m_filter) {
m_filter->set_in_and_out(0, length - 1);
m_producer->attach(*m_filter.get());
}
m_showFrameEvent.reset(m_consumer->listen("consumer-frame-show", this, (mlt_listener)consumer_frame_render));
......@@ -261,20 +216,7 @@ bool MeltJob::startJob()
return false;
});
m_consumer->run();
/*
QMap<QString, QString> jobResults;
if (m_jobStatus != JobAborted && m_extra.contains(QStringLiteral("key"))) {
QString result = QString::fromLatin1(m_filter->get(m_extra.value(QStringLiteral("key")).toUtf8().constData()));
jobResults.insert(m_extra.value(QStringLiteral("key")), result);
}
if (!jobResults.isEmpty() && m_jobStatus != JobAborted) {
emit gotFilterJobResults(m_clipId, startPos, track, jobResults, m_extra);
}
if (m_jobStatus == JobWorking) {
m_jobStatus = JobDone;
}
*/
qDebug()<<"===============FILTER PROCESSED\n\n==============0";
m_successful = m_done = true;
return true;
}
......@@ -162,6 +162,11 @@ Mlt::Tractor *TimelineController::tractor()
return m_model->tractor();
}
Mlt::Producer TimelineController::trackProducer(int tid)
{
return *(m_model->getTrackById(tid).get());
}
double TimelineController::scaleFactor() const
{
return m_scale;
......
......@@ -319,6 +319,9 @@ public:
/* @brief Return the project's tractor
*/
Mlt::Tractor *tractor();
/* @brief Return a track's producer
*/
Mlt::Producer trackProducer(int tid);
/* @brief Get the list of currently selected clip id's
*/
QList<int> selection() const;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment