Turn Stabilize filter in to a clip job

parent 79cb3b67
......@@ -6,7 +6,7 @@
<parameter type="double" name="gain" max="50" min="-50" default="5.00" decimals="2">
<name>Gain</name>
</parameter>
<parameter type="filterjob" filtertag="sox:analysis" filterparams="" consumer="null" consumerparams="video_off=1 all=1" wantedproperties="gain" finalfilter="sox_gain">
<parameter type="filterjob" filtertag="sox:analysis" filterparams="" consumer="null" consumerparams="video_off=1 all=1 terminate_on_pause=1" wantedproperties="gain" finalfilter="sox_gain">
<name>Normalize</name>
</parameter>
</effect>
......@@ -33,18 +33,16 @@
#include <QSlider>
#include <KFileDialog>
ClipStabilize::ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter* filter,QWidget * parent) :
QDialog(parent), m_profile(NULL),m_consumer(NULL),m_playlist(NULL),m_urls(urls),m_duration(0),m_filter(filter),vbox(NULL)
ClipStabilize::ClipStabilize(const QString &dest, int count, const QString &filterName,QWidget * parent) :
QDialog(parent),
m_filtername(filterName),
m_count(count),
vbox(NULL)
{
setFont(KGlobalSettings::toolBarFont());
setupUi(this);
if (filter) setAttribute(Qt::WA_DeleteOnClose);
log_text->setHidden(true);
setWindowTitle(i18n("Stabilize Clip"));
auto_add->setText(i18np("Add clip to project", "Add clips to project", m_urls.count()));
m_profile = new Mlt::Profile(KdenliveSettings::current_profile().toUtf8().constData());
filtername = params;
auto_add->setText(i18np("Add clip to project", "Add clips to project", count));
QPalette p = palette();
KColorScheme scheme(p.currentColorGroup(), KColorScheme::View, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
......@@ -62,40 +60,25 @@ ClipStabilize::ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter
.arg(dark_bg.name()).arg(selected_bg.name()).arg(hover_bg.name()).arg(light_bg.name()));
setStyleSheet(stylesheet);
if (m_urls.count() == 1) {
QString fileName = m_urls.at(0).path(); //.section('.', 0, -1);
QString newFile = fileName.append(".mlt");
if (m_count == 1) {
QString newFile = dest;
newFile.append(".mlt");
KUrl dest(newFile);
source_url->setUrl(m_urls.at(0));
dest_url->setMode(KFile::File);
dest_url->setUrl(dest);
dest_url->setUrl(KUrl(newFile));
dest_url->fileDialog()->setOperationMode(KFileDialog::Saving);
urls_list->setHidden(true);
connect(source_url, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateParams()));
} else {
label_source->setHidden(true);
source_url->setHidden(true);
label_dest->setText(i18n("Destination folder"));
dest_url->setMode(KFile::Directory);
dest_url->setUrl(KUrl(m_urls.at(0).directory()));
dest_url->setUrl(KUrl(dest));
dest_url->fileDialog()->setOperationMode(KFileDialog::Saving);
for (int i = 0; i < m_urls.count(); i++)
urls_list->addItem(m_urls.at(i).path());
}
if (!params.isEmpty()) {
label_profile->setHidden(true);
profile_list->setHidden(true);
//ffmpeg_params->setPlainText(params.simplified());
/*if (!description.isEmpty()) {
transcode_info->setText(description);
} else transcode_info->setHidden(true);*/
}
if (filtername=="videostab"){
if (m_filtername=="videostab"){
QStringList ls;
ls << "shutterangle,type,int,value,0,min,0,max,100,tooltip,Angle that Images could be maximum rotated";
fillParameters(ls);
}else if (filtername=="videostab2"){
}else if (m_filtername=="videostab2"){
QStringList ls;
ls << "accuracy,type,int,value,4,min,1,max,10,tooltip,Accuracy of Shakiness detection";
ls << "shakiness,type,int,value,4,min,1,max,10,tooltip,How shaky is the Video";
......@@ -115,11 +98,8 @@ ClipStabilize::ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter
fillParameters(ls);
}
connect(button_start, SIGNAL(clicked()), this, SLOT(slotStartStabilize()));
connect(buttonBox,SIGNAL(rejected()), this, SLOT(slotAbortStabilize()));
m_timer=new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(slotShowStabilizeInfo()));
//connect(buttonBox,SIGNAL(rejected()), this, SLOT(slotAbortStabilize()));
vbox=new QVBoxLayout(optionsbox);
QHashIterator<QString,QHash<QString,QString> > hi(m_ui_params);
......@@ -152,19 +132,49 @@ ClipStabilize::~ClipStabilize()
/*if (m_stabilizeProcess.state() != QProcess::NotRunning) {
m_stabilizeProcess.close();
}*/
if (m_stabilizeRun.isRunning()){
if (m_consumer){
m_consumer->stop();
}
m_stabilizeRun.waitForFinished();
}
QStringList ClipStabilize::params()
{
//we must return a stringlist with:
// producerparams << filtername << filterparams << consumer << consumerparams
QStringList params;
// producer params
params << QString();
// filter
params << m_filtername;
QStringList filterparamsList;
QHashIterator <QString,QHash<QString,QString> > it(m_ui_params);
while (it.hasNext()){
it.next();
filterparamsList << it.key() + "=" + it.value().value("value");
}
if (m_profile) free (m_profile);
if (m_consumer) free (m_consumer);
if (m_playlist) free (m_playlist);
params << filterparamsList.join(" ");
// consumer
params << "xml";
// consumer params
QString title = i18n("Stabilised");
params << QString("all=1 title=\"%1\"").arg(title);
return params;
}
QString ClipStabilize::destination() const
{
if (m_count == 1)
return dest_url->url().path();
else
return dest_url->url().directory(KUrl::AppendTrailingSlash);
}
QString ClipStabilize::desc() const
{
return i18n("Stabilize clip");
}
void ClipStabilize::slotStartStabilize()
{
/*
if (m_consumer && !m_consumer->is_stopped()) {
return;
}
......@@ -213,76 +223,10 @@ void ClipStabilize::slotStartStabilize()
button_start->setEnabled(false);
}
}
}
void ClipStabilize::slotRunStabilize()
{
if (m_consumer)
{
m_consumer->run();
}
}
void ClipStabilize::slotAbortStabilize()
{
if (m_consumer)
{
m_timer->stop();
m_consumer->stop();
slotStabilizeFinished(false);
}
}
void ClipStabilize::slotShowStabilizeInfo()
{
if (m_playlist){
job_progress->setValue((int) (100.0 * m_consumer->position()/m_playlist->get_out() ));
if (m_consumer->position()==m_playlist->get_out()){
m_timer->stop();
slotStabilizeFinished(true);
}
}
*/
}
void ClipStabilize::slotStabilizeFinished(bool success)
{
buttonBox->button(QDialogButtonBox::Abort)->setText(i18n("Close"));
button_start->setEnabled(true);
m_duration = 0;
if (m_stabilizeRun.isRunning()){
if (m_consumer){
m_consumer->stop();
}
m_stabilizeRun.waitForFinished();
}
if (success) {
log_text->setHtml(log_text->toPlainText() + "<br /><b>" + i18n("Stabilize finished."));
if (auto_add->isChecked()) {
KUrl url;
if (urls_list->count() > 0) {
url = KUrl(dest_url->url().path(KUrl::AddTrailingSlash) + source_url->url().fileName()+".mlt");
} else url = dest_url->url();
emit addClip(url);
}
if (urls_list->count() > 0 && m_urls.count() > 0) {
slotStartStabilize();
return;
} else if (auto_close->isChecked()) accept();
} else {
log_text->setHtml(log_text->toPlainText() + "<br /><b>" + i18n("Stabilizing FAILED!"));
}
if (m_playlist){
free(m_playlist);
m_playlist=NULL;
}
if (m_consumer){
free(m_consumer);
m_consumer=NULL;
}
}
void ClipStabilize::slotUpdateParams()
{
......
......@@ -42,30 +42,25 @@ class ClipStabilize : public QDialog, public Ui::ClipStabilize_UI
Q_OBJECT
public:
ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter* filter = NULL,QWidget * parent = 0);
ClipStabilize(const QString &dest, int count, const QString &filterName,QWidget * parent = 0);
~ClipStabilize();
/** @brief Should the generated clip be added to current project. */
bool autoAddClip() const;
/** @brief Return the filter parameters. */
QStringList params();
/** @brief Return the destination file or folder. */
QString destination() const;
/** @brief Return the job description. */
QString desc() const;
private slots:
void slotShowStabilizeInfo();
void slotStartStabilize();
void slotStabilizeFinished(bool success);
void slotRunStabilize();
void slotAbortStabilize();
void slotUpdateParams();
private:
QFuture<void> m_stabilizeRun;
QString filtername;
Mlt::Profile *m_profile;
Mlt::Consumer *m_consumer;
Mlt::Playlist *m_playlist;
KUrl::List m_urls;
int m_duration;
Mlt::Filter* m_filter;
QTimer *m_timer;
QString m_filtername;
int m_count;
QHash<QString,QHash<QString,QString> > m_ui_params;
QVBoxLayout *vbox;
void fillParameters(QStringList);
......
......@@ -54,7 +54,6 @@
#include "interfaces.h"
#include "config-kdenlive.h"
#include "cliptranscode.h"
#include "clipstabilize.h"
#include "ui_templateclip_ui.h"
#include "colorscopes/vectorscope.h"
#include "colorscopes/waveform.h"
......@@ -3890,27 +3889,33 @@ void MainWindow::loadTranscoders()
}
}
void MainWindow::slotStabilize(KUrl::List urls)
void MainWindow::slotStabilize()
{
QString condition,filtername;
QStringList ids;
if (urls.isEmpty()) {
QAction *action = qobject_cast<QAction *>(sender());
if (action){
filtername=action->data().toString();
urls = m_projectList->getConditionalUrls(condition);
}
// Stablize selected clips
QAction *action = qobject_cast<QAction *>(sender());
if (action){
filtername=action->data().toString();
}
if (urls.isEmpty()) {
m_projectList->startClipFilterJob(filtername, condition);
/*
if (ids.isEmpty()) {
m_messageLabel->setMessage(i18n("No clip to transcode"), ErrorMessage);
return;
}
Mlt::Profile profile;
Mlt::Filter filter(profile,filtername.toUtf8().data());
ClipStabilize *d=new ClipStabilize(urls,filtername,&filter);
connect(d, SIGNAL(addClip(KUrl)), this, SLOT(slotAddProjectClip(KUrl)));
d->show();
QString destination;
ProjectItem *item = m_projectList->getClipById(ids.at(0));
if (ids.count() == 1) {
}
ClipStabilize *d = new ClipStabilize(destination, ids.count(), filtername);
//connect(d, SIGNAL(addClip(KUrl)), this, SLOT(slotAddProjectClip(KUrl)));
if (d->exec() == QDialog::Accepted) {
m_projectList->slotStabilizeClipJob(ids, d->autoAddClip(), d->params(), d->desc());
}
delete d;*/
}
void MainWindow::slotTranscode(KUrl::List urls)
......@@ -3924,8 +3929,7 @@ void MainWindow::slotTranscode(KUrl::List urls)
params = data.at(0);
if (data.count() > 1) desc = data.at(1);
if (data.count() > 3) condition = data.at(3);
QStringList ids = m_projectList->getConditionalIds(condition);
m_projectList->slotTranscodeClipJob(ids, params, desc);
m_projectList->slotTranscodeClipJob(condition, params, desc);
return;
}
if (urls.isEmpty()) {
......
......@@ -482,7 +482,7 @@ private slots:
void slotShowTimeline(bool show);
void slotMaximizeCurrent(bool show);
void slotTranscode(KUrl::List urls = KUrl::List());
void slotStabilize(KUrl::List urls = KUrl::List());
void slotStabilize();
void slotTranscodeClip();
/** @brief Archive project: creates a copy of the project file with all clips in a new folder. */
void slotArchiveProject();
......
......@@ -36,6 +36,7 @@
#include "projectlistview.h"
#include "timecodedisplay.h"
#include "profilesdialog.h"
#include "clipstabilize.h"
#include "commands/editclipcommand.h"
#include "commands/editclipcutcommand.h"
#include "commands/editfoldercommand.h"
......@@ -2730,13 +2731,16 @@ void ProjectList::slotCutClipJob(const QString &id, QPoint zone)
slotCheckJobProcess();
}
void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString desc)
void ProjectList::slotTranscodeClipJob(const QString &condition, QString params, QString desc)
{
QStringList existingFiles;
QStringList ids = getConditionalIds(condition);
QStringList destinations;
foreach(const QString &id, ids) {
ProjectItem *item = getItemById(id);
if (!item) continue;
QString newFile = params.section(' ', -1).replace("%1", item->clipUrl().path());
destinations << newFile;
if (QFile::exists(newFile)) existingFiles << newFile;
}
if (!existingFiles.isEmpty()) {
......@@ -2747,30 +2751,36 @@ void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString
Ui::CutJobDialog_UI ui;
ui.setupUi(d);
d->setWindowTitle(i18n("Transcoding"));
ui.destination_label->setVisible(false);
ui.extra_params->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
ui.file_url->setVisible(false);
if (ids.count() == 1) {
ui.file_url->setUrl(KUrl(destinations.first()));
}
else {
ui.destination_label->setVisible(false);
ui.file_url->setVisible(false);
}
ui.extra_params->setVisible(false);
d->adjustSize();
ui.button_more->setIcon(KIcon("configure"));
ui.add_clip->setChecked(KdenliveSettings::add_clip_cut());
ui.extra_params->setPlainText(params.simplified());
ui.extra_params->setPlainText(params.simplified().section(" ", 0, -2));
QString mess = desc;
mess.append(" " + i18np("(%1 clip)", "(%1 clips)", ids.count()));
ui.info_label->setText(mess);
d->adjustSize();
if (d->exec() != QDialog::Accepted) {
delete d;
return;
}
params = ui.extra_params->toPlainText().simplified();
KdenliveSettings::setAdd_clip_cut(ui.add_clip->isChecked());
delete d;
foreach(const QString &id, ids) {
ProjectItem *item = getItemById(id);
if (!item || !item->referencedClip()) continue;
QString src = item->clipUrl().path();
QString dest = params.section(' ', -1).replace("%1", src);
QString dest;
if (ids.count() > 1) dest = params.section(' ', -1).replace("%1", src);
else dest = ui.file_url->url().path();
QStringList jobParams;
jobParams << dest << src << QString() << QString();
double clipFps = item->referencedClip()->getProperty("fps").toDouble();
......@@ -2779,7 +2789,7 @@ void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString
QString duration = QString::number(max);
jobParams << duration;
jobParams << QString::number(KdenliveSettings::add_clip_cut());
jobParams << params.section(' ', 0, -2);
jobParams << params;
CutClipJob *job = new CutClipJob(item->clipType(), id, jobParams);
if (job->isExclusive() && hasPendingJob(item, job->jobType)) {
delete job;
......@@ -2788,12 +2798,11 @@ void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString
m_jobList.append(job);
setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage());
}
delete d;
slotCheckJobProcess();
}
void ProjectList::slotCheckJobProcess()
{
if (!m_jobThreads.futures().isEmpty()) {
......@@ -2896,7 +2905,7 @@ void ProjectList::slotProcessJobs()
emit updateJobStatus(job->clipId(), job->jobType, JOBDONE);
//TODO: replace with more generic clip replacement framework
if (job->jobType == PROXYJOB) emit gotProxy(job->clipId());
if (job->addClipToProject) {
if (job->addClipToProject()) {
emit addClip(destination, QString(), QString());
}
} else if (job->jobStatus == JOBCRASHED || job->jobStatus == JOBABORTED) {
......@@ -3303,7 +3312,8 @@ void ProjectList::slotStartFilterJob(ItemInfo info, const QString&id, const QStr
if (!item) return;
QStringList jobParams;
jobParams << QString::number(info.cropStart.frames(m_fps)) << QString::number((info.cropStart + info.cropDuration).frames(m_fps));
jobParams << filterName << filterParams << consumer << consumerParams << properties << QString::number(info.startPos.frames(m_fps)) << QString::number(info.track) << finalFilterName;
jobParams << QString() << filterName << filterParams << consumer << consumerParams << properties << QString::number(info.startPos.frames(m_fps)) << QString::number(info.track) << finalFilterName;
kDebug()<<"// JPB PARAMS:"<<jobParams;
MeltJob *job = new MeltJob(item->clipType(), id, jobParams);
if (job->isExclusive() && hasPendingJob(item, job->jobType)) {
delete job;
......@@ -3314,6 +3324,69 @@ void ProjectList::slotStartFilterJob(ItemInfo info, const QString&id, const QStr
slotCheckJobProcess();
}
void ProjectList::startClipFilterJob(const QString &filterName, const QString &condition)
{
QStringList ids = getConditionalIds(condition);
QString destination;
ProjectItem *item = getItemById(ids.at(0));
if (!item) {
emit displayMessage(i18n("Cannot find clip to process filter %1", filterName), -2);
return;
}
if (ids.count() == 1) {
destination = item->clipUrl().path();
}
else {
destination = item->clipUrl().directory();
}
ClipStabilize *d = new ClipStabilize(destination, ids.count(), filterName);
if (d->exec() == QDialog::Accepted) {
processClipJob(ids, d->destination(), d->autoAddClip(), d->params(), d->desc());
}
delete d;
}
void ProjectList::processClipJob(QStringList ids, const QString&destination, bool autoAdd, QStringList jobParams, const QString &description)
{
QStringList preParams;
// in and out
preParams << QString::number(0) << QString::number(-1);
// producer params
preParams << jobParams.takeFirst();
// filter name
preParams << jobParams.takeFirst();
// filter params
preParams << jobParams.takeFirst();
// consumer
QString consumer = jobParams.takeFirst();
foreach(const QString&id, ids) {
ProjectItem *item = getItemById(id);
if (!item) continue;
if (ids.count() == 1) {
consumer += ":" + destination;
}
else {
consumer += ":" + destination + item->clipUrl().fileName() + ".mlt";
}
preParams << consumer << jobParams;
MeltJob *job = new MeltJob(item->clipType(), id, preParams);
if (autoAdd) {
job->setAddClipToProject(true);
kDebug()<<"// ADDING TRUE";
}
else kDebug()<<"// ADDING FALSE!!!";
if (job->isExclusive() && hasPendingJob(item, job->jobType)) {
delete job;
return;
}
m_jobList.append(job);
setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage());
}
slotCheckJobProcess();
}
void ProjectList::slotPrepareJobsMenu()
{
ProjectItem *item;
......
......@@ -257,9 +257,12 @@ public:
void clearSelection();
/** @brief Print required overlays over clip thumb (proxy, stabilized,...). */
void processThumbOverlays(ProjectItem *item, QPixmap &pix);
/** @brief Start an MLT process job. */
void startClipFilterJob(const QString &filterName, const QString &condition);
/** @brief Set current document for the project tree. */
void setDocument(KdenliveDoc *doc);
public slots:
void setDocument(KdenliveDoc *doc);
void updateAllClips(bool displayRatioChanged, bool fpsChanged, QStringList brokenClips);
void slotReplyGetImage(const QString &clipId, const QImage &img);
void slotReplyGetImage(const QString &clipId, const QString &name, int width, int height);
......@@ -297,9 +300,11 @@ public slots:
void slotDeleteProxy(const QString proxyPath);
/** @brief Start a hard cut clip job. */
void slotCutClipJob(const QString &id, QPoint zone);
void slotTranscodeClipJob(QStringList ids, QString params, QString desc);
/** @brief Start transcoding selected clips. */
void slotTranscodeClipJob(const QString &condition, QString params, QString desc);
/** @brief Start an MLT process job. */
void slotStartFilterJob(ItemInfo, const QString&,const QString&,const QString&,const QString&,const QString&,const QString&,const QString&);
private:
ProjectListView *m_listView;
......@@ -390,6 +395,8 @@ private:
void discardJobs(const QString &id, JOBTYPE type = NOJOBTYPE);
/** @brief Get the list of job names for current clip. */
QStringList getPendingJobs(const QString &id);
/** @brief Start an MLT process job. */
void processClipJob(QStringList ids, const QString&destination, bool autoAdd, QStringList jobParams, const QString &description);
private slots:
void slotClipSelected();
......
......@@ -31,9 +31,9 @@ AbstractClipJob::AbstractClipJob(JOBTYPE type, CLIPTYPE cType, const QString &id
clipType(cType),
jobType(type),
jobStatus(NOJOB),
m_clipId(id),
addClipToProject(false),
replaceClip(false),
m_clipId(id),
m_addClipToProject(false),
m_jobProcess(NULL)
{
}
......@@ -42,6 +42,17 @@ AbstractClipJob::~AbstractClipJob()
{
}
bool AbstractClipJob::addClipToProject() const
{
return m_addClipToProject;
}
void AbstractClipJob::setAddClipToProject(bool add)
{
m_addClipToProject = add;
}
void AbstractClipJob::setStatus(CLIPJOBSTATUS status)
{
jobStatus = status;
......
......@@ -37,9 +37,7 @@ public:
CLIPTYPE clipType;
JOBTYPE jobType;
CLIPJOBSTATUS jobStatus;
QString m_clipId;
QString description;
bool addClipToProject;
bool replaceClip;
const QString clipId() const;
const QString errorMessage() const;
......@@ -52,10 +50,14 @@ public:
virtual const QString statusMessage();
/** @brief Returns true if only one instance of this job can be run on a clip. */
virtual bool isExclusive();
bool addClipToProject() const;
void setAddClipToProject(bool add);