Commit 8db7e33c authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Fix subclip thumbs, port stabilize and speed jobs to new task framework

parent 566d96bf
......@@ -4056,7 +4056,7 @@ void Bin::reloadAllProducers(bool reloadThumbs)
clip->discardAudioThumb();
// We need to set a temporary id before all outdated producers are replaced;
//int jobId = pCore->jobManager()->startJob<LoadJob>({clip->clipId()}, -1, QString(), xml);
ClipLoadTask::start({ObjectType::BinClip,clip->clipId().toInt()}, xml, false, this);
ClipLoadTask::start({ObjectType::BinClip,clip->clipId().toInt()}, xml, false, -1, -1, this);
if (reloadThumbs) {
ThumbnailCache::get()->invalidateThumbsForClip(clip->clipId());
}
......@@ -4510,3 +4510,37 @@ void Bin::requestTranscoding(const QString &url, const QString &id)
m_transcodingDialog->addUrl(url, id);
m_transcodingDialog->show();
}
bool Bin::addProjectClipInFolder(const QString &path, const QString &parentFolder, const QString &folderName)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
// Check if folder exists
QString folderId = QStringLiteral("-1");
bool found = false;
// We first try to see if it exists
std::shared_ptr<ProjectFolder> baseFolder = m_itemModel->getFolderByBinId(parentFolder);
if (!baseFolder) {
baseFolder = m_itemModel->getRootFolder();
}
for (int i = 0; i < baseFolder->childCount(); ++i) {
auto currentItem = std::static_pointer_cast<AbstractProjectItem>(baseFolder->child(i));
if (currentItem->itemType() == AbstractProjectItem::FolderItem && currentItem->name() == folderName) {
found = true;
folderId = currentItem->clipId();
break;
}
}
if (!found) {
// if it was not found, create folder
m_itemModel->requestAddFolder(folderId, folderName, parentFolder, undo, redo);
}
auto id = ClipCreator::createClipFromFile(path, folderId, m_itemModel, undo, redo);
bool ok = (id != QStringLiteral("-1"));
if (ok) {
pCore->pushUndo(undo, redo, i18nc("@action", "Add clip"));
}
return ok;
}
......@@ -461,6 +461,8 @@ public slots:
*/
void checkProjectAudioTracks(QString clipId, int minimumTracksCount);
void showTitleWidget(const std::shared_ptr<ProjectClip> &clip);
/** @brief Add a clip in a specially named folder */
bool addProjectClipInFolder(const QString &path, const QString &parentFolder, const QString &folderName);
protected:
/* This function is called whenever an item is selected to propagate signals
......
......@@ -123,7 +123,7 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, const std::share
connectEffectStack();
if (m_clipStatus == FileStatus::StatusProxy || m_clipStatus == FileStatus::StatusReady || m_clipStatus == FileStatus::StatusProxyOnly) {
// Generate clip thumbnail
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, this);
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, -1, -1, this);
// Generate audio thumbnail
AudioLevelsTask::start({ObjectType::BinClip, m_binId.toInt()}, this, false);
}
......@@ -259,7 +259,7 @@ void ProjectClip::updateAudioThumbnail()
// Cache thumbnail
ThumbnailCache::get()->storeThumbnail(m_binId, 0, thumb, true);
}
setThumbnail(thumb);
setThumbnail(thumb, -1, -1);
}
if (!KdenliveSettings::audiothumbnails()) {
return;
......@@ -381,7 +381,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
//pCore->jobManager()->discardJobs(clipId(), AbstractClipJob::THUMBJOB);
pCore->taskManager.discardJobs({ObjectType::BinClip, m_binId.toInt()}, AbstractTask::LOADJOB);
m_thumbsProducer.reset();
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, this);
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, -1, -1, this);
//emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadjobId, QString(), -1, true, true);
} else {
// If another load job is running?
......@@ -413,7 +413,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
if (forceAudioReload || (!isProxy && hashChanged)) {
discardAudioThumb();
}
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, xml, false, this);
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, xml, false, -1, -1, this);
//int loadJob = pCore->jobManager()->startJob<LoadJob>({clipId()}, loadjobId, QString(), xml);
//emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadJob, QString(), -1, true, true);
}
......@@ -435,11 +435,18 @@ QDomElement ProjectClip::toXml(QDomDocument &document, bool includeMeta, bool in
return prod;
}
void ProjectClip::setThumbnail(const QImage &img)
void ProjectClip::setThumbnail(const QImage &img, int in, int out)
{
if (img.isNull()) {
return;
}
if (in > -1) {
std::shared_ptr<ProjectSubClip> sub = getSubClip(in, out);
if (sub) {
sub->setThumbnail(img);
}
return;
}
QPixmap thumb = roundedPixmap(QPixmap::fromImage(img));
if (hasProxy() && !thumb.isNull()) {
// Overlay proxy icon
......@@ -529,7 +536,7 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool repl
getFileHash();
// set parent again (some info need to be stored in producer)
updateParent(parentItem().lock());
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, this);
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, -1, -1, this);
AudioLevelsTask::start({ObjectType::BinClip, m_binId.toInt()}, this, false);
if (pCore->currentDoc()->getDocumentProperty(QStringLiteral("enableproxy")).toInt() == 1) {
......@@ -1781,7 +1788,7 @@ void ProjectClip::getThumbFromPercent(int percent)
int framePos = duration * percent / 100;
framePos -= framePos%steps;
if (ThumbnailCache::get()->hasThumbnail(m_binId, framePos)) {
setThumbnail(ThumbnailCache::get()->getThumbnail(m_binId, framePos));
setThumbnail(ThumbnailCache::get()->getThumbnail(m_binId, framePos), -1, -1);
} else {
// Generate percent thumbs
int id;
......
......@@ -277,7 +277,7 @@ public slots:
void updateJobProgress();
/** @brief Sets thumbnail for this clip. */
void setThumbnail(const QImage &);
void setThumbnail(const QImage &, int in, int out);
void setThumbProducer(std::shared_ptr<Mlt::Producer>prod);
/**
......
......@@ -729,7 +729,7 @@ bool ProjectItemModel::requestAddBinClip(QString &id, const QDomElement &descrip
ProjectClip::construct(id, description, m_blankThumb, std::static_pointer_cast<ProjectItemModel>(shared_from_this()));
bool res = addItem(new_clip, parentId, undo, redo);
if (res) {
ClipLoadTask::start({ObjectType::BinClip,id.toInt()}, description, false, this);
ClipLoadTask::start({ObjectType::BinClip,id.toInt()}, description, false, -1, -1, this);
//int loadJob = emit pCore->jobManager()->startJob<LoadJob>({id}, -1, QString(), description, std::bind(readyCallBack, id));
int loadJob = -1;
//emit pCore->jobManager()->startJob<ThumbJob>({id}, loadJob, QString(), 0, true);
......
......@@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "bincommands.h"
#include "jobs/jobmanager.h"
#include "jobs/cachejob.hpp"
#include "jobs/cliploadtask.h"
#include "utils/thumbnailcache.hpp"
#include <KLocalizedString>
......@@ -59,8 +60,7 @@ ProjectSubClip::ProjectSubClip(const QString &id, const std::shared_ptr<ProjectC
m_tags = zoneProperties.value(QLatin1String("tags"));
qDebug()<<"=== LOADING SUBCLIP WITH RATING: "<<m_rating<<", TAGS: "<<m_tags;
m_clipStatus = FileStatus::StatusReady;
// Save subclip in MLT
connect(parent.get(), &ProjectClip::thumbReady, this, &ProjectSubClip::gotThumb);
ClipLoadTask::start({ObjectType::BinClip,m_parentClipId.toInt()}, QDomElement(), true, in, out, this);
}
std::shared_ptr<ProjectSubClip> ProjectSubClip::construct(const QString &id, const std::shared_ptr<ProjectClip> &parent,
......
......@@ -113,7 +113,6 @@ void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unor
}
}
QDomNodeList effects = doc.elementsByTagName(QStringLiteral("effect"));
int nbr_effect = effects.count();
if (nbr_effect == 0) {
qWarning() << "broken effect:" << file_name;
......@@ -129,8 +128,10 @@ void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unor
Info result;
bool ok = parseInfoFromXml(currentEffect, result);
if (!ok) {
qDebug()<<"==== PARSING ABORTED FOR: "<<file_name;
continue;
}
if (customAssets.count(result.id) > 0) {
//qDebug() << "duplicate effect" << result.id;
}
......
......@@ -7,6 +7,8 @@ set(kdenlive_SRCS
jobs/audiolevelstask.cpp
jobs/cliploadtask.cpp
jobs/proxytask.cpp
jobs/stabilizetask.cpp
jobs/speedtask.cpp
jobs/transcodetask.cpp
jobs/filtertask.cpp
jobs/jobmanager.cpp
......@@ -14,8 +16,6 @@ set(kdenlive_SRCS
jobs/loadjob.cpp
jobs/meltjob.cpp
jobs/scenesplitjob.cpp
jobs/speedjob.cpp
jobs/stabilizejob.cpp
jobs/thumbjob.cpp
jobs/transcodeclipjob.cpp
jobs/cutclipjob.cpp
......
......@@ -47,9 +47,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KMessageWidget>
ClipLoadTask::ClipLoadTask(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, QObject* object, std::function<void()> readyCallBack)
ClipLoadTask::ClipLoadTask(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, int in, int out, QObject* object, std::function<void()> readyCallBack)
: AbstractTask(owner, AbstractTask::LOADJOB, object)
, m_xml(xml)
, m_in(in)
, m_out(out)
, m_thumbOnly(thumbOnly)
, m_readyCallBack(std::move(readyCallBack))
{
......@@ -59,10 +61,10 @@ ClipLoadTask::~ClipLoadTask()
{
}
void ClipLoadTask::start(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, QObject* object, bool force, std::function<void()> readyCallBack)
void ClipLoadTask::start(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, int in, int out, QObject* object, bool force, std::function<void()> readyCallBack)
{
ClipLoadTask* task = new ClipLoadTask(owner, xml, thumbOnly, object, readyCallBack);
if (pCore->taskManager.hasPendingJob(owner, AbstractTask::LOADJOB)) {
ClipLoadTask* task = new ClipLoadTask(owner, xml, thumbOnly, in, out, object, readyCallBack);
if (!thumbOnly && pCore->taskManager.hasPendingJob(owner, AbstractTask::LOADJOB)) {
delete task;
task = 0;
}
......@@ -234,13 +236,13 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip>binClip, std::s
{
// Fetch thumbnail
qDebug()<<"===== \nREADY FOR THUMB\n\n=========";
int frameNumber = qMax(0, binClip->getProducerIntProperty(QStringLiteral("kdenlive:thumbnailFrame")));
int frameNumber = m_in > -1 ? m_in : qMax(0, binClip->getProducerIntProperty(QStringLiteral("kdenlive:thumbnailFrame")));
if (binClip->clipType() != ClipType::Audio) {
if (ThumbnailCache::get()->hasThumbnail(QString::number(m_owner.second), frameNumber, false)) {
// Thumbnail found in cache
QImage result = ThumbnailCache::get()->getThumbnail(QString::number(m_owner.second), frameNumber);
qDebug()<<"=== FOUND THUMB IN CACHe";
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage,result));
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage,result), Q_ARG(int,m_in), Q_ARG(int,m_out));
} else {
QString mltService = producer->get("mlt_service");
const QString mltResource = producer->get("resource");
......@@ -282,9 +284,10 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip>binClip, std::s
QPainter p(&result);
p.setPen(Qt::white);
p.drawText(0, 0, fullWidth, imageHeight, Qt::AlignCenter, i18n("Invalid"));
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage,result));
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage,result), Q_ARG(int,m_in), Q_ARG(int,m_out));
} else {
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage,result));
qDebug()<<"=== GOT THUMB FOR: "<<m_in<<"x"<<m_out;
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage,result), Q_ARG(int,m_in), Q_ARG(int,m_out));
ThumbnailCache::get()->storeThumbnail(QString::number(m_owner.second), frameNumber, result, true);
}
}
......
......@@ -37,9 +37,9 @@ class ProjectClip;
class ClipLoadTask : public AbstractTask
{
public:
ClipLoadTask(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, QObject* object, std::function<void()> readyCallBack);
ClipLoadTask(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, int in, int out, QObject* object, std::function<void()> readyCallBack);
virtual ~ClipLoadTask();
static void start(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, QObject* object, bool force = false, std::function<void()> readyCallBack = []() {});
static void start(const ObjectId &owner, const QDomElement &xml, bool thumbOnly, int in, int out, QObject* object, bool force = false, std::function<void()> readyCallBack = []() {});
static ClipType::ProducerType getTypeForService(const QString &id, const QString &path);
std::shared_ptr<Mlt::Producer> loadResource(QString resource, const QString &type);
std::shared_ptr<Mlt::Producer> loadPlaylist(QString &resource);
......@@ -54,6 +54,8 @@ protected:
private:
//QString cacheKey();
QDomElement m_xml;
int m_in;
int m_out;
bool m_thumbOnly;
std::function<void()> m_readyCallBack;
QString m_errorMessage;
......
......@@ -60,16 +60,6 @@ void FilterTask::start(const ObjectId &owner, const QString &binId, std::weak_pt
}
}
void FilterTask::updateProgress(int prog)
{
if (prog != m_progress) {
m_progress = prog;
if (auto ptr = m_model.lock()) {
QMetaObject::invokeMethod(ptr.get(), "setProgress", Q_ARG(int, prog));
}
}
}
void FilterTask::run()
{
if (m_isCanceled) {
......
......@@ -42,7 +42,6 @@ class FilterTask : public AbstractTask
public:
FilterTask(const ObjectId &owner, const QString &binId, std::weak_ptr<AssetParameterModel> model, const QString &assetId, int in, int out, QString filterName, std::unordered_map<QString, QVariant> filterParams, std::unordered_map<QString, QString> filterData, const QStringList consumerArgs, QObject* object);
static void start(const ObjectId &owner, const QString &binId, std::weak_ptr<AssetParameterModel> model, const QString &assetId, int in, int out, QString filterName, std::unordered_map<QString, QVariant> filterParams, std::unordered_map<QString, QString> filterData, const QStringList consumerArgs, QObject* object, bool force = false);
void updateProgress(int prog);
int length;
private slots:
......
/***************************************************************************
* *
* Copyright (C) 2021 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "speedtask.h"
#include "bin/bin.h"
#include "mainwindow.h"
#include "bin/projectclip.h"
#include "bin/projectfolder.h"
#include "project/clipstabilize.h"
#include "bin/projectitemmodel.h"
#include "profiles/profilemodel.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "core.h"
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
#include "macros.hpp"
#include "xml/xml.hpp"
#include <QThread>
#include <QProcess>
#include <KIO/RenameDialog>
#include <klocalizedstring.h>
#include <KLineEdit>
SpeedTask::SpeedTask(const ObjectId &owner, const QString &binId, const QString &destination, int in, int out, std::unordered_map<QString, QVariant> filterParams, QObject* object)
: AbstractTask(owner, AbstractTask::SPEEDJOB, object)
, m_binId(binId)
, m_filterParams(filterParams)
, m_destination(destination)
{
m_speed = filterParams.at(QStringLiteral("warp_speed")).toDouble();
m_inPoint = qRound(in / m_speed);
m_outPoint = qRound(out / m_speed);
}
void SpeedTask::start(QObject* object, bool force)
{
std::vector<QString> binIds = pCore->bin()->selectedClipsIds(true);
// Show config dialog
QDialog d(qApp->activeWindow());
d.setWindowTitle(i18n("Clip Speed"));
QDialogButtonBox buttonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
auto *l = new QVBoxLayout;
d.setLayout(l);
QLabel labUrl(&d);
KUrlRequester fileUrl(&d);
auto binClip = pCore->projectItemModel()->getClipByBinID(binIds.front().section(QLatin1Char('/'), 0, 0));
QDir folder = QFileInfo(binClip->url()).absoluteDir();
folder.mkpath(i18n("Speed Change"));
folder.cd(i18n("Speed Change"));
if (binIds.size() > 1) {
labUrl.setText(i18n("Destination Folder"));
fileUrl.setMode(KFile::Directory);
fileUrl.setUrl(QUrl::fromLocalFile(folder.absolutePath()));
} else {
labUrl.setText(i18n("Destination File"));
fileUrl.setMode(KFile::File);
QString filePath = QFileInfo(binClip->url()).fileName().section(QLatin1Char('.'), 0, -2);
filePath.append(QStringLiteral(".mlt"));
fileUrl.setUrl(QUrl::fromLocalFile(folder.absoluteFilePath(filePath)));
}
QFontMetrics fm = fileUrl.lineEdit()->fontMetrics();
fileUrl.setMinimumWidth(int(fm.boundingRect(fileUrl.text().left(50)).width() * 1.4));
QLabel lab(&d);
lab.setText(i18n("Percentage"));
QDoubleSpinBox speedInput(&d);
speedInput.setRange(-100000, 100000);
speedInput.setValue(100);
speedInput.setSuffix(QLatin1String("%"));
speedInput.setFocus();
speedInput.selectAll();
QCheckBox cb(i18n("Pitch compensation"), &d);
cb.setChecked(true);
l->addWidget(&labUrl);
l->addWidget(&fileUrl);
l->addWidget(&lab);
l->addWidget(&speedInput);
l->addWidget(&cb);
l->addWidget(&buttonBox);
d.connect(&buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
d.connect(&buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
if (d.exec() != QDialog::Accepted) {
return;
}
double speed = speedInput.value();
bool warp_pitch = cb.isChecked();
std::unordered_map<QString, QString> destinations; // keys are binIds, values are path to target files
std::unordered_map<QString, QVariant> filterParams;
filterParams[QStringLiteral("warp_speed")] = speed / 100.0;
if (warp_pitch) {
filterParams[QStringLiteral("warp_pitch")] = 1;
}
for (const auto &binId : binIds) {
QString mltfile;
if (binIds.size() == 1) {
// converting only 1 clip
mltfile = fileUrl.url().toLocalFile();
} else {
QDir dir(fileUrl.url().toLocalFile());
binClip = pCore->projectItemModel()->getClipByBinID(binId.section(QLatin1Char('/'), 0, 0));
mltfile = QFileInfo(binClip->url()).fileName().section(QLatin1Char('.'), 0, -2);
mltfile.append(QString("-%1.mlt").arg(QString::number(int(speed))));
mltfile = dir.absoluteFilePath(mltfile);
}
// Filter several clips, destination points to a folder
if (QFile::exists(mltfile)) {
KIO::RenameDialog renameDialog(qApp->activeWindow(), i18n("File already exists"), QUrl::fromLocalFile(mltfile), QUrl::fromLocalFile(mltfile), KIO::RenameDialog_Option::RenameDialog_Overwrite );
if (renameDialog.exec() != QDialog::Rejected) {
QUrl final = renameDialog.newDestUrl();
if (final.isValid()) {
mltfile = final.toLocalFile();
}
} else {
return;
}
}
destinations[binId] = mltfile;
}
for (auto & id : binIds) {
SpeedTask* task = nullptr;
ObjectId owner;
if (id.contains(QLatin1Char('/'))) {
QStringList binData = id.split(QLatin1Char('/'));
if (binData.size() < 3) {
// Invalid subclip data
qDebug()<<"=== INVALID SUBCLIP DATA: "<<id;
continue;
}
owner = ObjectId(ObjectType::BinClip, binData.first().toInt());
auto binClip = pCore->projectItemModel()->getClipByBinID(binData.first());
if (binClip) {
task = new SpeedTask(owner, binData.first(), destinations.at(id), binData.at(1).toInt(), binData.at(2).toInt(), filterParams, binClip.get());
}
} else {
// Process full clip
owner = ObjectId(ObjectType::BinClip, id.toInt());
auto binClip = pCore->projectItemModel()->getClipByBinID(id);
if (binClip) {
task = new SpeedTask(owner, id, destinations.at(id), -1, -1, filterParams, binClip.get());
}
}
if (task) {
// Otherwise, start a filter thread.
task->m_isForce = force;
pCore->taskManager.startTask(owner.second, task);
}
}
}
void SpeedTask::run()
{
if (m_isCanceled) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
m_running = true;
qDebug()<<" + + + + + + + + STARTING STAB TASK";
QString url;
auto binClip = pCore->projectItemModel()->getClipByBinID(m_binId);
QStringList producerArgs = {QStringLiteral("progress=1"),QStringLiteral("-profile"),pCore->getCurrentProfilePath()};
if (binClip) {
// Filter applied on a timeline or bin clip
url = binClip->url();
if (url.isEmpty()) {
m_errorMessage.append(i18n("No producer for this clip."));
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
producerArgs << QString("timewarp:%1:%2").arg(m_speed).arg(url);
if (m_inPoint > -1) {
producerArgs << QString("in=%1").arg(m_inPoint);
}
if (m_outPoint > -1) {
producerArgs << QString("out=%1").arg(m_outPoint);
}
} else {
// Filter applied on a track of master producer, leave config to source job
// We are on master or track, configure producer accordingly
// TODO
/*if (m_owner.first == ObjectType::Master) {
producer = pCore->getMasterProducerInstance();
} else if (m_owner.first == ObjectType::TimelineTrack) {
producer = pCore->getTrackProducerInstance(m_owner.second);
}
if ((producer == nullptr) || !producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
m_errorMessage.append(i18n("Invalid clip"));
pCore->taskManager.taskDone(m_owner.second, this);
return;
}*/
}
// Process filter params
for (const auto &it : m_filterParams) {
qDebug()<<". . ."<<it.first<<" = "<<it.second;
if (it.second.type() == QVariant::Double) {
producerArgs << QString("%1=%2").arg(it.first).arg(it.second.toDouble());
} else {
producerArgs << QString("%1=%2").arg(it.first).arg(it.second.toString());
}
}
// Start the MLT Process
QProcess filterProcess;
producerArgs << QStringLiteral("-consumer") << QString("xml:%1").arg(m_destination) << QStringLiteral("terminate_on_pause=1");
m_jobProcess.reset(new QProcess);
QMetaObject::invokeMethod(m_object, "updateJobProgress");
QObject::connect(this, &AbstractTask::jobCanceled, m_jobProcess.get(), &QProcess::kill, Qt::DirectConnection);
QObject::connect(m_jobProcess.get(), &QProcess::readyReadStandardError, this, &SpeedTask::processLogInfo);
qDebug()<<"=== STARTING PROCESS: "<<producerArgs;
m_jobProcess->start(KdenliveSettings::rendererpath(), producerArgs);
m_jobProcess->waitForFinished(-1);
qDebug()<<" + + + + + + + + SOURCE FILE PROCESSED: "<<m_jobProcess->exitStatus();
bool result = m_jobProcess->exitStatus() == QProcess::NormalExit;
m_progress = 100;
QMetaObject::invokeMethod(m_object, "updateJobProgress");
pCore->taskManager.taskDone(m_owner.second, this);
if (m_isCanceled || !result) {
return;
}
QMetaObject::invokeMethod(pCore->bin(), "addProjectClipInFolder", Qt::QueuedConnection, Q_ARG(const QString&,m_destination), Q_ARG(const QString&,binClip->parent()->clipId()), Q_ARG(const QString&,i18n("Speed Change")));
return;
}
void SpeedTask::processLogInfo()
{
const QString buffer = QString::fromUtf8(m_jobProcess->readAllStandardError());
m_logDetails.append(buffer);
// Parse MLT output
if (buffer.contains(QLatin1String("percentage:"))) {
int progress = buffer.section(QStringLiteral("percentage:"), 1).simplified().section(QLatin1Char(' '), 0, 0).toInt();
if (progress == m_progress) {
return;
}
m_progress = progress;
QMetaObject::invokeMethod(m_object, "updateJobProgress");
}
}
/***************************************************************************
* *
* Copyright (C) 2021 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *