]> git.sesse.net Git - kdenlive/commitdiff
Improvements to the clip jobs framework
authorJean-Baptiste Mardelle <jb@kdenlive.org>
Thu, 29 Dec 2011 13:46:09 +0000 (14:46 +0100)
committerJean-Baptiste Mardelle <jb@kdenlive.org>
Thu, 29 Dec 2011 13:46:09 +0000 (14:46 +0100)
20 files changed:
src/clipstabilize.cpp
src/clipstabilize.h
src/effectstackedit.cpp
src/effectstackedit.h
src/effectstackview.cpp
src/effectstackview.h
src/mainwindow.cpp
src/projectlist.cpp
src/projectlist.h
src/projectlistview.cpp
src/projecttree/CMakeLists.txt
src/projecttree/abstractclipjob.cpp
src/projecttree/abstractclipjob.h
src/projecttree/cutclipjob.cpp
src/projecttree/cutclipjob.h
src/projecttree/meltjob.cpp [new file with mode: 0644]
src/projecttree/meltjob.h [new file with mode: 0644]
src/projecttree/proxyclipjob.cpp
src/projecttree/proxyclipjob.h
src/renderwidget.h

index 0702f77c49bd52ef176c51913e1d646f4a410e18..67ad688a94cbaa1639c473e3474476541309707a 100644 (file)
@@ -38,22 +38,22 @@ ClipStabilize::ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter
 {
     setFont(KGlobalSettings::toolBarFont());
     setupUi(this);
-    setAttribute(Qt::WA_DeleteOnClose);
+    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;
+    m_profile = new Mlt::Profile(KdenliveSettings::current_profile().toUtf8().constData());
+    filtername = params;
 
 
-       QPalette p = palette();
-       KColorScheme scheme(p.currentColorGroup(), KColorScheme::View, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
-       QColor dark_bg = scheme.shade(KColorScheme::DarkShade);
-       QColor selected_bg = scheme.decoration(KColorScheme::FocusColor).color();
-       QColor hover_bg = scheme.decoration(KColorScheme::HoverColor).color();
-       QColor light_bg = scheme.shade(KColorScheme::LightShade);
+    QPalette p = palette();
+    KColorScheme scheme(p.currentColorGroup(), KColorScheme::View, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
+    QColor dark_bg = scheme.shade(KColorScheme::DarkShade);
+    QColor selected_bg = scheme.decoration(KColorScheme::FocusColor).color();
+    QColor hover_bg = scheme.decoration(KColorScheme::HoverColor).color();
+    QColor light_bg = scheme.shade(KColorScheme::LightShade);
 
-       QString stylesheet(QString("QProgressBar:horizontal {border: 1px solid %1;border-radius:0px;border-top-left-radius: 4px;border-bottom-left-radius: 4px;border-right: 0px;background:%4;padding: 0px;text-align:left center}\
+    QString stylesheet(QString("QProgressBar:horizontal {border: 1px solid %1;border-radius:0px;border-top-left-radius: 4px;border-bottom-left-radius: 4px;border-right: 0px;background:%4;padding: 0px;text-align:left center}\
                                QProgressBar:horizontal#dragOnly {background: %1} QProgressBar:horizontal:hover#dragOnly {background: %3} QProgressBar:horizontal:hover {border: 1px solid %3;border-right: 0px;}\
                                QProgressBar::chunk:horizontal {background: %1;} QProgressBar::chunk:horizontal:hover {background: %3;}\
                                QProgressBar:horizontal[inTimeline=\"true\"] { border: 1px solid %2;border-right: 0px;background: %4;padding: 0px;text-align:left center } QProgressBar::chunk:horizontal[inTimeline=\"true\"] {background: %2;}\
@@ -301,6 +301,11 @@ void ClipStabilize::slotUpdateParams()
        }
 }
 
+bool ClipStabilize::autoAddClip() const
+{
+    return auto_add->isChecked();
+}
+
 void ClipStabilize::fillParameters(QStringList lst)
 {
 
index 1e80b44afca84fd25ac2e43d8357ec4064f3e86b..3fe0600d765d08201108e46e89cf847b88fdbf4b 100644 (file)
@@ -42,31 +42,33 @@ class ClipStabilize : public QDialog, public Ui::ClipStabilize_UI
     Q_OBJECT
 
 public:
-    ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter* filter,QWidget * parent = 0);
+    ClipStabilize(KUrl::List urls, const QString &params, Mlt::Filter* filter = NULL,QWidget * parent = 0);
     ~ClipStabilize();
+    /** @brief Should the generated clip be added to current project. */
+    bool autoAddClip() const;
 
 
 private slots:
     void slotShowStabilizeInfo();
     void slotStartStabilize();
     void slotStabilizeFinished(bool success);
-       void slotRunStabilize();
-       void slotAbortStabilize();
-       void slotUpdateParams();
+    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;
+    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;
-       QHash<QString,QHash<QString,QString> > m_ui_params;
-       QVBoxLayout *vbox;
-       void fillParameters(QStringList);
+    Mlt::Filter* m_filter;
+    QTimer *m_timer;
+    QHash<QString,QHash<QString,QString> > m_ui_params;
+    QVBoxLayout *vbox;
+    void fillParameters(QStringList);
 
 signals:
     void addClip(KUrl url);
index 7efab0f218314f9592ec2efe3a8bcfcd2c325180..c274a8f81398f51363b19457f3aba4fddeed6d23 100644 (file)
@@ -262,7 +262,6 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
         QString comment;
         if (!commentElem.isNull())
             comment = i18n(commentElem.text().toUtf8().data());
-        QWidget * toFillin = new QWidget(m_baseWidget);
         QString value = pa.attribute("value").isNull() ?
                         pa.attribute("default") : pa.attribute("value");
 
@@ -289,7 +288,9 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
             connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
         } else if (type == "list") {
             Listval *lsval = new Listval;
+            QWidget * toFillin = new QWidget(m_baseWidget);
             lsval->setupUi(toFillin);
+            m_vbox->addWidget(toFillin);
             QStringList listitems = pa.attribute("paramlist").split(';');
             if (listitems.count() == 1) {
                 // probably custom effect created before change to ';' as separator
@@ -327,7 +328,9 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
             m_uiItems.append(lsval);
         } else if (type == "bool") {
             Boolval *bval = new Boolval;
+            QWidget * toFillin = new QWidget(m_baseWidget);
             bval->setupUi(toFillin);
+            m_vbox->addWidget(toFillin);
             bval->checkBox->setCheckState(value == "0" ? Qt::Unchecked : Qt::Checked);
             bval->name->setText(paramName);
             bval->labelComment->setText(comment);
@@ -479,7 +482,9 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
 #endif
         } else if (type == "wipe") {
             Wipeval *wpval = new Wipeval;
+            QWidget * toFillin = new QWidget(m_baseWidget);
             wpval->setupUi(toFillin);
+            m_vbox->addWidget(toFillin);
             wipeInfo w = getWipeInfo(value);
             switch (w.start) {
             case UP:
@@ -534,7 +539,9 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
             m_uiItems.append(wpval);
         } else if (type == "url") {
             Urlval *cval = new Urlval;
+            QWidget * toFillin = new QWidget(m_baseWidget);
             cval->setupUi(toFillin);
+            m_vbox->addWidget(toFillin);
             cval->label->setText(paramName);
             cval->urlwidget->fileDialog()->setFilter(ProjectList::getExtensions());
             m_valueItems[paramName] = cval;
@@ -544,7 +551,9 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
             m_uiItems.append(cval);
         } else if (type == "keywords") {
             Keywordval* kval = new Keywordval;
+            QWidget * toFillin = new QWidget(m_baseWidget);
             kval->setupUi(toFillin);
+            m_vbox->addWidget(toFillin);
             kval->label->setText(paramName);
             kval->lineeditwidget->setText(value);
             QDomElement klistelem = pa.firstChildElement("keywords");
@@ -571,19 +580,20 @@ void EffectStackEdit::transferParamDesc(const QDomElement &d, ItemInfo info, boo
             m_uiItems.append(kval);
         } else if (type == "fontfamily") {
             Fontval* fval = new Fontval;
+            QWidget * toFillin = new QWidget(m_baseWidget);
             fval->setupUi(toFillin);
+            m_vbox->addWidget(toFillin);
             fval->name->setText(paramName);
             fval->fontfamilywidget->setCurrentFont(QFont(value));
             m_valueItems[paramName] = fval;
             connect(fval->fontfamilywidget, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(collectAllParameters())) ;
             m_uiItems.append(fval);
-        } else {
-            delete toFillin;
-            toFillin = NULL;
+        } else if (type == "filterjob") {
+            QPushButton *button = new QPushButton(paramName, m_baseWidget);
+            m_vbox->addWidget(button);
+            m_valueItems[paramName] = button;
+            connect(button, SIGNAL(pressed()), this, SLOT(slotStartFilterJobAction()));   
         }
-
-        if (toFillin)
-            m_vbox->addWidget(toFillin);
     }
 
     if (stretch)
@@ -892,3 +902,18 @@ void EffectStackEdit::slotSyncEffectsPos(int pos)
 {
     emit syncEffectsPos(pos);
 }
+
+void EffectStackEdit::slotStartFilterJobAction()
+{
+    QDomNodeList namenode = m_params.elementsByTagName("parameter");
+    for (int i = 0; i < namenode.count() ; i++) {
+        QDomElement pa = namenode.item(i).toElement();
+        QString type = pa.attribute("type");
+        if (type == "filterjob") {
+            emit startFilterJob(pa.attribute("filtertag"), pa.attribute("filterparams"), pa.attribute("consumer"), pa.attribute("consumerparams"), pa.attribute("wantedproperties"));
+            kDebug()<<" - - -PROPS:\n"<<pa.attribute("filtertag")<<"-"<< pa.attribute("filterparams")<<"-"<< pa.attribute("consumer")<<"-"<< pa.attribute("consumerparams")<<"-"<< pa.attribute("wantedproperties");
+            break;
+        }
+    }
+}
+
index ea41b30ff0125f11edc4f68a1ad9dc6e43a1a52f..645672d62322c5d66574155cdaee6e26fb84b099 100644 (file)
@@ -96,6 +96,9 @@ public slots:
 
     /** @brief Pass position changes of the timeline cursor to the effects to keep their local timelines in sync. */
     void slotSyncEffectsPos(int pos);
+    
+private slots:
+    void slotStartFilterJobAction();
 
 signals:
     void parameterChanged(const QDomElement &, const QDomElement &);
@@ -105,6 +108,8 @@ signals:
     void syncEffectsPos(int pos);
     void showComments(bool show);
     void effectStateChanged(bool enabled);
+    /** @brief Start an MLT filter job on this clip. */
+    void startFilterJob(const QString &filterName, const QString &filterParams, const QString &consumer, const QString &consumerParams, const QString &properties);
 };
 
 #endif
index 11f09cca007e0fcf5e4b20efcc7a4e1f3f6d65dd..2c7b41cb6b9a9bb296e7093b9e1545543800319a 100644 (file)
@@ -92,6 +92,7 @@ EffectStackView::EffectStackView(Monitor *monitor, QWidget *parent) :
     connect(m_ui.checkAll, SIGNAL(stateChanged(int)), this, SLOT(slotCheckAll(int)));
     connect(m_ui.buttonShowComments, SIGNAL(clicked()), this, SLOT(slotShowComments()));
     connect(m_effectedit, SIGNAL(parameterChanged(const QDomElement &, const QDomElement &)), this , SLOT(slotUpdateEffectParams(const QDomElement &, const QDomElement &)));
+    connect(m_effectedit, SIGNAL(startFilterJob(QString,QString,QString,QString,QString)), this , SLOT(slotStartFilterJob(QString,QString,QString,QString,QString)));
     connect(m_effectedit, SIGNAL(seekTimeline(int)), this , SLOT(slotSeekTimeline(int)));
     connect(m_effectedit, SIGNAL(displayMessage(const QString&, int)), this, SIGNAL(displayMessage(const QString&, int)));
     connect(m_effectedit, SIGNAL(checkMonitorPosition(int)), this, SLOT(slotCheckMonitorPosition(int)));
@@ -528,4 +529,10 @@ void EffectStackView::slotShowComments()
     emit showComments(m_ui.buttonShowComments->isChecked());
 }
 
+void EffectStackView::slotStartFilterJob(const QString&filterName, const QString&filterParams, const QString&consumer, const QString&consumerParams, const QString&properties)
+{
+    if (!m_clipref) return;
+    emit startFilterJob(m_clipref->clipProducer(), filterName, filterParams, consumer, consumerParams, properties);
+}
+
 #include "effectstackview.moc"
index 89055a2360374e03a9e691fa12cbe4c4903e6cb2..e3412bd45fcee53996366f622c243a380dae5ed8 100644 (file)
@@ -141,6 +141,9 @@ private slots:
 
     /** @brief Shows/Hides the comment box and emits showComments to notify the parameter widgets to do the same. */
     void slotShowComments();
+    
+    /** @brief Triggers a filter job on this clip. */
+    void slotStartFilterJob(const QString&filterName, const QString&filterParams, const QString&consumer, const QString&consumerParams, const QString&properties);
 
 signals:
     void removeEffect(ClipItem*, int, QDomElement);
@@ -161,6 +164,7 @@ signals:
     void updateClipRegion(ClipItem*, int, QString);
     void displayMessage(const QString&, int);
     void showComments(bool show);
+    void startFilterJob(const QString &clipId, const QString &filterName, const QString &filterParams, const QString &consumer, const QString &consumerParams, const QString &properties);
 };
 
 #endif
index 927df0a90b33a12a7ba7eb2c64f6415eebc2cff1..164d5fe5fef5f0a57a0c7ce609fe4cd090a02c9f 100644 (file)
@@ -269,6 +269,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
     m_effectStack = new EffectStackView(m_projectMonitor);
     m_effectStackDock->setWidget(m_effectStack);
     addDockWidget(Qt::TopDockWidgetArea, m_effectStackDock);
+    connect(m_effectStack, SIGNAL(startFilterJob(const QString&,const QString&,const QString&,const QString&,const QString&,const QString&)), m_projectList, SLOT(slotStartFilterJob(const QString&,const QString&,const QString&,const QString&,const QString&,const QString&)));
 
     m_transitionConfigDock = new QDockWidget(i18n("Transition"), this);
     m_transitionConfigDock->setObjectName("transition");
@@ -3847,14 +3848,14 @@ void MainWindow::loadTranscoders()
 
 void MainWindow::slotStabilize(KUrl::List urls)
 {
-       QString condition,filtername;
+    QString condition,filtername;
 
-       if (urls.isEmpty()) {
+    if (urls.isEmpty()) {
         QAction *action = qobject_cast<QAction *>(sender());
-               if (action){
-                       filtername=action->data().toString();
-                       urls = m_projectList->getConditionalUrls(condition);
-               }
+       if (action){
+            filtername=action->data().toString();
+            urls = m_projectList->getConditionalUrls(condition);
+       }
     }
     if (urls.isEmpty()) {
         m_messageLabel->setMessage(i18n("No clip to transcode"), ErrorMessage);
index 4e78eecbe82362b2bb83657b0d1d9625cf077e13..fb481bd7366856523a4daa7cac6decc10d64baad 100644 (file)
@@ -22,6 +22,7 @@
 #include "commands/addfoldercommand.h"
 #include "projecttree/proxyclipjob.h"
 #include "projecttree/cutclipjob.h"
+#include "projecttree/meltjob.h"
 #include "kdenlivesettings.h"
 #include "slideshowclip.h"
 #include "ui_colorclip_ui.h"
@@ -232,10 +233,16 @@ ProjectList::ProjectList(QWidget *parent) :
     m_infoLabel = new SmallInfoLabel(this);
     connect(this, SIGNAL(jobCount(int)), m_infoLabel, SLOT(slotSetJobCount(int)));
     m_jobsMenu = new QMenu(this);
+    connect(m_jobsMenu, SIGNAL(aboutToShow()), this, SLOT(slotPrepareJobsMenu()));
     QAction *cancelJobs = new QAction(i18n("Cancel All Jobs"), this);
     cancelJobs->setCheckable(false);
     connect(cancelJobs, SIGNAL(triggered()), this, SLOT(slotCancelJobs()));
+    connect(this, SIGNAL(checkJobProcess()), this, SLOT(slotCheckJobProcess()));
+    m_discardCurrentClipJobs = new QAction(i18n("Cancel Current Clip Jobs"), this);
+    m_discardCurrentClipJobs->setCheckable(false);
+    connect(m_discardCurrentClipJobs, SIGNAL(triggered()), this, SLOT(slotDiscardClipJobs()));
     m_jobsMenu->addAction(cancelJobs);
+    m_jobsMenu->addAction(m_discardCurrentClipJobs);
     m_infoLabel->setMenu(m_jobsMenu);
     box->addWidget(m_infoLabel);
        
@@ -295,7 +302,6 @@ ProjectList::ProjectList(QWidget *parent) :
     
     connect(this, SIGNAL(updateJobStatus(const QString &, int, int, const QString &, const QString &, const QString)), this, SLOT(slotUpdateJobStatus(const QString &, int, int, const QString &, const QString &, const QString)));
     
-    connect(this, SIGNAL(checkJobProcess()), this, SLOT(slotCheckJobProcess()));
     connect(this, SIGNAL(gotProxy(const QString)), this, SLOT(slotGotProxyForId(const QString)));
     
     m_listViewDelegate = new ItemDelegate(m_listView);
@@ -314,6 +320,9 @@ ProjectList::ProjectList(QWidget *parent) :
 ProjectList::~ProjectList()
 {
     m_abortAllJobs = true;
+    for (int i = 0; i < m_jobList.count(); i++) {
+        m_jobList.at(i)->setStatus(JOBABORTED);
+    }
     m_closing = true;
     m_thumbnailQueue.clear();
     m_jobThreads.waitForFinished();
@@ -659,7 +668,7 @@ void ProjectList::slotReloadClip(const QString &id)
             continue;
         }
         item = static_cast <ProjectItem *>(selected.at(i));
-        if (item && !hasPendingProxy(item)) {
+        if (item && !hasPendingJob(item, PROXYJOB)) {
             DocClipBase *clip = item->referencedClip();
             if (!clip || !clip->isClean() || m_render->isProcessing(item->clipId())) {
                 kDebug()<<"//// TRYING TO RELOAD: "<<item->clipId()<<", but it is busy";
@@ -1376,6 +1385,9 @@ void ProjectList::slotResetProjectList()
 {
     m_listView->blockSignals(true);
     m_abortAllJobs = true;
+    for (int i = 0; i < m_jobList.count(); i++) {
+        m_jobList.at(i)->setStatus(JOBABORTED);
+    }
     m_closing = true;
     m_jobThreads.waitForFinished();
     m_jobThreads.clearFutures();
@@ -1493,7 +1505,7 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged, QStr
                     clip->setProperty("proxy", "-");
                     replace = true;
                 }
-                if (clip->isPlaceHolder() == false && !hasPendingProxy(item)) {
+                if (clip->isPlaceHolder() == false && !hasPendingJob(item, PROXYJOB)) {
                     QDomElement xml = clip->toXML();
                     if (fpsChanged) {
                         xml.removeAttribute("out");
@@ -1667,7 +1679,6 @@ void ProjectList::slotAddClip(const QList <QUrl> givenList, const QString &group
 void ProjectList::slotRemoveInvalidClip(const QString &id, bool replace)
 {
     ProjectItem *item = getItemById(id);
-    m_processingClips.removeAll(id);
     m_thumbnailQueue.removeAll(id);
     if (item) {
         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
@@ -1726,7 +1737,6 @@ void ProjectList::slotRemoveInvalidProxy(const QString &id, bool durationError)
             item->referencedClip()->reloadThumbProducer();
         }
     }
-    m_processingClips.removeAll(id);
     m_thumbnailQueue.removeAll(id);
 }
 
@@ -1836,12 +1846,14 @@ void ProjectList::setDocument(KdenliveDoc *doc)
 {
     m_listView->blockSignals(true);
     m_abortAllJobs = true;
+    for (int i = 0; i < m_jobList.count(); i++) {
+        m_jobList.at(i)->setStatus(JOBABORTED);
+    }
     m_closing = true;
     m_jobThreads.waitForFinished();
     m_jobThreads.clearFutures();
     m_thumbnailQueue.clear();
     m_listView->clear();
-    m_processingClips.clear();
     
     m_listView->setSortingEnabled(false);
     emit clipSelected(NULL);
@@ -2048,7 +2060,7 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce
             item->setConditionalJobStatus(NOJOB, PROXYJOB);
             discardJobs(clipId, PROXYJOB);
         }
-        else if (useProxy() && !item->hasProxy() && !hasPendingProxy(item)) {
+        else if (useProxy() && !item->hasProxy() && !hasPendingJob(item, PROXYJOB)) {
             // proxy video and image clips
             int maxSize;
             CLIPTYPE t = item->clipType();
@@ -2568,26 +2580,26 @@ QMap <QString, QString> ProjectList::getProxies()
 void ProjectList::slotCreateProxy(const QString id)
 {
     ProjectItem *item = getItemById(id);
-    if (!item || hasPendingProxy(item) || item->referencedClip()->isPlaceHolder()) return;
+    if (!item || hasPendingJob(item, PROXYJOB) || item->referencedClip()->isPlaceHolder()) return;
     QString path = item->referencedClip()->getProperty("proxy");
     if (path.isEmpty()) {
         slotUpdateJobStatus(item, PROXYJOB, JOBCRASHED, i18n("Failed to create proxy, empty path."));
         return;
     }
 
-    if (m_processingProxy.contains(path)) {
-        // Proxy is already being generated
+    ProxyJob *job = new ProxyJob(item->clipType(), id, QStringList() << path << item->clipUrl().path() << item->referencedClip()->producerProperty("_exif_orientation") << m_doc->getDocumentProperty("proxyparams").simplified() << QString::number(m_render->frameRenderWidth()) << QString::number(m_render->renderHeight()));
+    if (job->isExclusive() && hasPendingJob(item, job->jobType)) {
+        delete job;
         return;
     }
+
     if (QFileInfo(path).size() > 0) {
         // Proxy already created
         setJobStatus(item, PROXYJOB, JOBDONE);
         slotGotProxy(path);
         return;
     }
-    m_processingProxy.append(path);
 
-    ProxyJob *job = new ProxyJob(item->clipType(), id, QStringList() << path << item->clipUrl().path() << item->referencedClip()->producerProperty("_exif_orientation") << m_doc->getDocumentProperty("proxyparams").simplified() << QString::number(m_render->frameRenderWidth()) << QString::number(m_render->renderHeight()));
     m_jobList.append(job);
     setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage());
     slotCheckJobProcess();
@@ -2653,11 +2665,14 @@ void ProjectList::slotCutClipJob(const QString &id, QPoint zone)
     KdenliveSettings::setAdd_clip_cut(ui.add_clip->isChecked());
     delete d;
 
-    m_processingProxy.append(dest);
     QStringList jobParams;
     jobParams << dest << item->clipUrl().path() << timeIn << timeOut << QString::number(duration) << QString::number(KdenliveSettings::add_clip_cut());
     if (!extraParams.isEmpty()) jobParams << extraParams;
     CutClipJob *job = new CutClipJob(item->clipType(), id, jobParams);
+    if (job->isExclusive() && hasPendingJob(item, job->jobType)) {
+        delete job;
+        return;
+    }
     m_jobList.append(job);
     setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage());
 
@@ -2705,7 +2720,6 @@ void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString
         if (!item || !item->referencedClip()) continue;
         QString src = item->clipUrl().path();
         QString dest = params.section(' ', -1).replace("%1", src);
-        m_processingProxy.append(dest);
         QStringList jobParams;
         jobParams << dest << src << QString() << QString();
         double clipFps = item->referencedClip()->getProperty("fps").toDouble();
@@ -2716,6 +2730,10 @@ void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString
         jobParams << QString::number(KdenliveSettings::add_clip_cut());
         jobParams << params.section(' ', 0, -2);
         CutClipJob *job = new CutClipJob(item->clipType(), id, jobParams);
+        if (job->isExclusive() && hasPendingJob(item, job->jobType)) {
+            delete job;
+            continue;
+        }
         m_jobList.append(job);
         setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage());
     }
@@ -2724,6 +2742,7 @@ void ProjectList::slotTranscodeClipJob(QStringList ids, QString params, QString
 }
 
 
+
 void ProjectList::slotCheckJobProcess()
 {        
     if (!m_jobThreads.futures().isEmpty()) {
@@ -2801,86 +2820,35 @@ void ProjectList::slotProcessJobs()
         emit processLog(job->clipId(), 0, job->jobType, job->statusMessage()); 
 
         // Make sure destination path is writable
-        QFile file(destination);
-        if (!file.open(QIODevice::WriteOnly)) {
-            emit updateJobStatus(job->clipId(), job->jobType, JOBCRASHED, i18n("Cannot write to path: %1", destination));
-            m_processingProxy.removeAll(destination);
-            job->setStatus(JOBCRASHED);
-            continue;
-        }
-        file.close();
-        QFile::remove(destination);
-    
-        //setJobStatus(processingItem, JOBWORKING, 0, job->jobType, job->statusMessage());
-
-        bool success;
-        QProcess *jobProcess = job->startJob(&success);
-        int result = -1;
-        if (jobProcess == NULL) {
-            // job is finished
-            if (success) {
-                emit updateJobStatus(job->clipId(), job->jobType, JOBDONE);
-                if (job->jobType == PROXYJOB) emit gotProxy(job->clipId());
-                //TODO: set folder for transcoded clips
-                else if (job->jobType == CUTJOB) emit addClip(destination, QString(), QString());
-                job->setStatus(JOBDONE);
-            }
-            else {
-                QFile::remove(destination);
-                emit updateJobStatus(job->clipId(), job->jobType, JOBCRASHED, job->errorMessage());
+        if (!destination.isEmpty()) {
+            QFile file(destination);
+            if (!file.open(QIODevice::WriteOnly)) {
+                emit updateJobStatus(job->clipId(), job->jobType, JOBCRASHED, i18n("Cannot write to path: %1", destination));
                 job->setStatus(JOBCRASHED);
-            }
-            m_processingProxy.removeAll(destination);
-            continue;
-        }
-        else while (jobProcess->state() != QProcess::NotRunning) {
-            // building proxy file
-            if (m_abortAllJobs || job->jobStatus == JOBABORTED) {
-                job->jobStatus = JOBABORTED;
-                jobProcess->close();
-                jobProcess->waitForFinished();
-                QFile::remove(destination);
-                m_processingProxy.removeAll(destination);
-                if (!m_closing) {
-                    emit cancelRunningJob(job->clipId(), job->cancelProperties());
-                }
-                result = -2;
                 continue;
             }
-            else {
-                int progress = job->processLogInfo();
-                if (progress > 0) emit processLog(job->clipId(), progress, job->jobType); 
-            }
-            jobProcess->waitForFinished(500);
+            file.close();
+            QFile::remove(destination);
         }
-        jobProcess->waitForFinished();
-        m_processingProxy.removeAll(destination);
-        if (result == -1) result = jobProcess->exitStatus();
-        
-        if (result != -2 && QFileInfo(destination).size() == 0) {
-            job->processLogInfo();
-            emit updateJobStatus(job->clipId(), job->jobType, JOBCRASHED, i18n("Failed to create file"), QString(), job->errorMessage());
-            job->setStatus(JOBCRASHED);
-            result = -2;
-        }
-        if (result == QProcess::NormalExit) {
-            // proxy successfully created
+        connect(job, SIGNAL(jobProgress(QString, int, int)), this, SIGNAL(processLog(QString, int, int)));
+        connect(job, SIGNAL(cancelRunningJob(const QString, stringMap)), this, SIGNAL(cancelRunningJob(const QString, stringMap)));
+
+        if (job->jobType == MLTJOB) {
+            MeltJob *jb = static_cast<MeltJob *> (job);
+            jb->setProducer(processingItem->referencedClip()->getProducer());
+        }
+        job->startJob();
+        if (job->jobStatus == JOBDONE) {
             emit updateJobStatus(job->clipId(), job->jobType, JOBDONE);
+            //TODO: replace with more generic clip replacement framework
             if (job->jobType == PROXYJOB) emit gotProxy(job->clipId());
-            //TODO: set folder for transcoded clips
-            else if (job->jobType == CUTJOB) {
-                CutClipJob *cutJob = static_cast<CutClipJob *>(job);
-                if (cutJob->addClipToProject) emit addClip(destination, QString(), QString());
+            if (job->addClipToProject) {
+                emit addClip(destination, QString(), QString());
             }
-            job->setStatus(JOBDONE);
         }
-        else if (result == QProcess::CrashExit) {
-            // Proxy process crashed
-            QFile::remove(destination);
-            emit updateJobStatus(job->clipId(), job->jobType, JOBCRASHED, i18n("Job crashed"), QString(), job->errorMessage());
-            job->setStatus(JOBCRASHED);
+        else if (job->jobStatus == JOBCRASHED) {
+            emit updateJobStatus(job->clipId(), job->jobType, JOBCRASHED, job->errorMessage());
         }
-        continue;
     }
     // Thread finished, cleanup & update count
     QTimer::singleShot(200, this, SIGNAL(checkJobProcess()));
@@ -2906,7 +2874,7 @@ void ProjectList::updateProxyConfig()
         }
         CLIPTYPE t = item->clipType();
         if ((t == VIDEO || t == AV || t == UNKNOWN) && item->referencedClip() != NULL) {
-            if  (generateProxy() && useProxy() && !hasPendingProxy(item)) {
+            if  (generateProxy() && useProxy() && !hasPendingJob(item, PROXYJOB)) {
                 DocClipBase *clip = item->referencedClip();
                 if (clip->getProperty("frame_size").section('x', 0, 0).toInt() > m_doc->getDocumentProperty("proxyminsize").toInt()) {
                     if (clip->getProperty("proxy").isEmpty()) {
@@ -3139,6 +3107,9 @@ void ProjectList::processThumbOverlays(ProjectItem *item, QPixmap &pix)
 void ProjectList::slotCancelJobs()
 {
     m_abortAllJobs = true;
+    for (int i = 0; i < m_jobList.count(); i++) {
+        m_jobList.at(i)->setStatus(JOBABORTED);
+    }
     m_jobThreads.waitForFinished();
     m_jobThreads.clearFutures();
     QUndoCommand *command = new QUndoCommand();
@@ -3158,7 +3129,6 @@ void ProjectList::slotCancelJobs()
     }
     else delete command;
     if (!m_jobList.isEmpty()) qDeleteAll(m_jobList);
-    m_processingProxy.clear();
     m_jobList.clear();
     m_abortAllJobs = false;
     m_infoLabel->slotSetJobCount(0);    
@@ -3166,7 +3136,7 @@ void ProjectList::slotCancelJobs()
 
 void ProjectList::slotCancelRunningJob(const QString id, stringMap newProps)
 {
-    if (newProps.isEmpty()) return;
+    if (newProps.isEmpty() || m_closing) return;
     ProjectItem *item = getItemById(id);
     if (!item || !item->referencedClip()) return;
     QMap <QString, QString> oldProps = item->referencedClip()->currentProperties(newProps);
@@ -3176,7 +3146,7 @@ void ProjectList::slotCancelRunningJob(const QString id, stringMap newProps)
     m_commandStack->push(command);    
 }
 
-bool ProjectList::hasPendingProxy(ProjectItem *item)
+bool ProjectList::hasPendingJob(ProjectItem *item, JOBTYPE type)
 {
     if (!item || !item->referencedClip() || m_abortAllJobs) return false;
     AbstractClipJob *job;
@@ -3184,7 +3154,7 @@ bool ProjectList::hasPendingProxy(ProjectItem *item)
     for (int i = 0; i < m_jobList.count(); i++) {
         if (m_abortAllJobs) break;
         job = m_jobList.at(i);
-        if (job->clipId() == item->clipId() && job->jobType == PROXYJOB && (job->jobStatus == JOBWAITING || job->jobStatus == JOBWORKING)) return true;
+        if (job->clipId() == item->clipId() && job->jobType == type && (job->jobStatus == JOBWAITING || job->jobStatus == JOBWORKING)) return true;
     }
     
     return false;
@@ -3194,7 +3164,6 @@ void ProjectList::deleteJobsForClip(const QString &clipId)
 {
     QMutexLocker lock(&m_jobMutex);
     for (int i = 0; i < m_jobList.count(); i++) {
-        if (m_abortAllJobs) break;
         if (m_jobList.at(i)->clipId() == clipId) {
             m_jobList.at(i)->setStatus(JOBABORTED);
         }
@@ -3255,14 +3224,71 @@ void ProjectList::slotShowJobLog()
 #endif
 }
 
+QStringList ProjectList::getPendingJobs(const QString &id)
+{
+    QStringList result;
+    QMutexLocker lock(&m_jobMutex);
+    for (int i = 0; i < m_jobList.count(); i++) {
+        if (m_jobList.at(i)->clipId() == id && (m_jobList.at(i)->jobStatus == JOBWAITING || m_jobList.at(i)->jobStatus == JOBWORKING)) {
+            // discard this job
+            result << m_jobList.at(i)->description;
+        }
+    }   
+    return result;
+}
+
 void ProjectList::discardJobs(const QString &id, JOBTYPE type) {
     QMutexLocker lock(&m_jobMutex);
     for (int i = 0; i < m_jobList.count(); i++) {
-        if (m_jobList.at(i)->clipId() == id && m_jobList.at(i)->jobType == type) {
+        if (m_jobList.at(i)->clipId() == id && (m_jobList.at(i)->jobType == type || type == NOJOBTYPE)) {
             // discard this job
             m_jobList.at(i)->setStatus(JOBABORTED);
         }
     }
 }
 
+void ProjectList::slotStartFilterJob(const QString&id, const QString&filterName, const QString&filterParams, const QString&consumer, const QString&consumerParams, const QString&properties)
+{
+    ProjectItem *item = getItemById(id);
+    if (!item) return;
+    QStringList jobParams;
+    jobParams << filterName << filterParams << consumer << consumerParams << properties;
+    kDebug()<<"// STARTING JOB: "<<jobParams;
+    MeltJob *job = new MeltJob(item->clipType(), id, jobParams);
+    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;
+    if (!m_listView->currentItem() || m_listView->currentItem()->type() == PROJECTFOLDERTYPE)
+        return;
+    if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE)
+        item = static_cast <ProjectItem*>(m_listView->currentItem()->parent());
+    else
+        item = static_cast <ProjectItem*>(m_listView->currentItem());
+    if (item && (item->flags() & Qt::ItemIsDragEnabled)) {
+        QString id = item->clipId();
+        m_discardCurrentClipJobs->setData(id);
+        QStringList jobs = getPendingJobs(id);
+        m_discardCurrentClipJobs->setEnabled(!jobs.isEmpty());
+    } else {
+        m_discardCurrentClipJobs->setData(QString());
+        m_discardCurrentClipJobs->setEnabled(false);
+    }
+}
+
+void ProjectList::slotDiscardClipJobs()
+{
+    QString id = m_discardCurrentClipJobs->data().toString();
+    if (id.isEmpty()) return;
+    discardJobs(id);
+}
+
 #include "projectlist.moc"
index d0f181ea1fe5557960ecc7604d4b586e91cbc2d0..1fb55de7b18b34d8b19144b1e2409e14954aebfc 100644 (file)
@@ -302,6 +302,8 @@ public slots:
     /** @brief Start a hard cut clip job. */
     void slotCutClipJob(const QString &id, QPoint zone);
     void slotTranscodeClipJob(QStringList ids, QString params, QString desc);
+    /** @brief Start an MLT process job. */
+    void slotStartFilterJob(const QString&,const QString&,const QString&,const QString&,const QString&,const QString&);
 
 private:
     ProjectListView *m_listView;
@@ -316,6 +318,7 @@ private:
     FolderProjectItem *getFolderItemById(const QString &id);
     QAction *m_openAction;
     QAction *m_reloadAction;
+    QAction *m_discardCurrentClipJobs;
     QMenu *m_extractAudioAction;
     QMenu *m_transcodeAction;
     QMenu *m_stabilizeAction;
@@ -332,9 +335,6 @@ private:
     QMap <QString, QDomElement> m_producerQueue;
     QList <QString> m_thumbnailQueue;
     QAction *m_proxyAction;
-    QStringList m_processingClips;
-    /** @brief Holds a list of proxy urls that are currently being created. */
-    QStringList m_processingProxy;
     QMutex m_jobMutex;
     bool m_abortAllJobs;
     /** @brief We are cleaning up the project list, so stop processing signals. */
@@ -385,14 +385,14 @@ private:
     void getCachedThumbnail(SubProjectItem *item);
     /** @brief The clip is about to be reloaded, cancel thumbnail requests. */
     void resetThumbsProducer(DocClipBase *clip);
-    /** @brief Check if it is necessary to start a job thread. */
-    void slotCheckJobProcess();
-    /** @brief Check if a clip has a running or pending proxy process. */
-    bool hasPendingProxy(ProjectItem *item);
+    /** @brief Check if a clip has a running or pending job process. */
+    bool hasPendingJob(ProjectItem *item, JOBTYPE type);
     /** @brief Delete pending jobs for a clip. */
     void deleteJobsForClip(const QString &clipId);
     /** @brief Discard specific job type for a clip. */
-    void discardJobs(const QString &id, JOBTYPE type);
+    void discardJobs(const QString &id, JOBTYPE type = NOJOBTYPE);
+    /** @brief Get the list of job names for current clip. */
+    QStringList getPendingJobs(const QString &id);
 
 private slots:
     void slotClipSelected();
@@ -446,6 +446,12 @@ private slots:
     void slotShowJobLog();
     /** @brief A proxy clip is ready. */
     void slotGotProxyForId(const QString);
+    /** @brief Check if it is necessary to start a job thread. */
+    void slotCheckJobProcess();
+    /** @brief Fill the jobs menu with current clip's jobs. */
+    void slotPrepareJobsMenu();
+    /** @brief Discard all jobs for current clip. */
+    void slotDiscardClipJobs();
 
 signals:
     void clipSelected(DocClipBase *, QPoint zone = QPoint(), bool forceUpdate = false);
@@ -475,8 +481,8 @@ signals:
     void processLog(const QString, int , int, const QString = QString());
     void addClip(const QString, const QString &, const QString &);
     void updateJobStatus(const QString, int, int, const QString &label = QString(), const QString &actionName = QString(), const QString details = QString());
-    void checkJobProcess();
     void gotProxy(const QString);
+    void checkJobProcess();
 };
 
 #endif
index e08fd4b743bfba42f63893c90c6e6526bae1478d..c68bccf219e4d2737560b49d967c674558223156 100644 (file)
@@ -304,7 +304,15 @@ void ProjectListView::mousePressEvent(QMouseEvent *event)
         m_DragStartPosition = event->pos();
         m_dragStarted = true;
         /*QTreeWidgetItem *underMouse = itemAt(event->pos());
-        if (underMouse && underMouse->isSelected()) emit focusMonitor();*/
+        ProjectItem *item = static_cast<ProjectItem *>(underMouse);
+        if (item) {
+            QRect itemRect = visualItemRect(item);
+            if (item->underJobMenu(itemRect, event->pos())) {
+                emit display
+            }
+            
+            && underMouse->isSelected()) emit focusMonitor()
+        }*/
     }
     QTreeWidget::mousePressEvent(event);
 }
index f05f9fcc060f1a46aa09a2020985fe8598db38cc..ae5a822552f2500d64fe223fd25db1eb5990a95c 100644 (file)
@@ -3,5 +3,6 @@ set(kdenlive_SRCS
   projecttree/abstractclipjob.cpp
   projecttree/proxyclipjob.cpp
   projecttree/cutclipjob.cpp
+  projecttree/meltjob.cpp
   PARENT_SCOPE
 )
index 81441db2601b821e8627b5db893424ec92d7550b..825c07241ea0a2748a5b7bf8de21bb3adfac7d6a 100644 (file)
@@ -32,6 +32,8 @@ AbstractClipJob::AbstractClipJob(JOBTYPE type, CLIPTYPE cType, const QString &id
         jobType(type),
         jobStatus(NOJOB),
         m_clipId(id),
+        addClipToProject(false),
+        replaceClip(false),
         m_jobProcess(NULL)
 {
 }
@@ -55,9 +57,8 @@ const QString AbstractClipJob::errorMessage() const
     return m_errorMessage;
 }
 
-QProcess *AbstractClipJob::startJob(bool */*ok*/)
+void AbstractClipJob::startJob()
 {
-    return NULL;
 }
 
 const QString AbstractClipJob::destination() const
@@ -70,9 +71,8 @@ stringMap AbstractClipJob::cancelProperties()
     return QMap <QString, QString>();
 }
 
-int AbstractClipJob::processLogInfo()
+void AbstractClipJob::processLogInfo()
 {
-    return -1;
 }
 
 const QString AbstractClipJob::statusMessage()
@@ -80,4 +80,8 @@ const QString AbstractClipJob::statusMessage()
     return QString();
 }
 
+bool AbstractClipJob::isExclusive()
+{
+    return true;
+}
 
index 7292e38b4ce49f93b962e59ec7528fb5bdbdf4d3..26c46a6ddaa2118b93a6c78eb3f67f560a9d3bbf 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "definitions.h"
 
-enum JOBTYPE { NOJOBTYPE = 0, PROXYJOB = 1, CUTJOB = 2};
+enum JOBTYPE { NOJOBTYPE = 0, PROXYJOB = 1, CUTJOB = 2, MLTJOB = 3};
 
 class AbstractClipJob : public QObject
 {
@@ -35,29 +35,30 @@ class AbstractClipJob : public QObject
 public:
     AbstractClipJob(JOBTYPE type, CLIPTYPE cType, const QString &id, QStringList parameters);    virtual ~ AbstractClipJob();
     CLIPTYPE clipType;
-    CLIPJOBSTATUS jobStatus;
     JOBTYPE jobType;
+    CLIPJOBSTATUS jobStatus;
     QString m_clipId;
     QString description;
+    bool addClipToProject;
+    bool replaceClip;
     const QString clipId() const;
     const QString errorMessage() const;
     void setStatus(CLIPJOBSTATUS status);
     virtual const QString destination() const;
-    virtual QProcess *startJob(bool */*ok*/);
+    virtual void startJob();
     virtual stringMap cancelProperties();
-    virtual int processLogInfo();
+    virtual void processLogInfo();
     virtual const QString statusMessage();
+    /** @brief Returns true if only one instance of this job can be run on a clip. */
+    virtual bool isExclusive();
     
 protected:
     QString m_errorMessage;
     QProcess *m_jobProcess;
     
-private:
-    
-    
 signals:
-    void jobProgress(int progress);
-
+    void jobProgress(QString, int, int);
+    void cancelRunningJob(const QString, stringMap);
 };
 
 
index 47c4daf000800bac55555d38bc6c4a5a01a9b09e..31caf5fe03c238641d0e41dc53b324a2f0a1e205 100644 (file)
@@ -39,10 +39,11 @@ CutClipJob::CutClipJob(CLIPTYPE cType, const QString &id, QStringList parameters
     else description = i18n("Cut clip");
     m_jobDuration = parameters.at(4).toInt();
     addClipToProject = parameters.at(5).toInt();
+    replaceClip = false;
     if (parameters.count() == 7) m_cutExtraParams = parameters.at(6).simplified();
 }
 
-QProcess *CutClipJob::startJob(bool *ok)
+void CutClipJob::startJob()
 {
     // Special case: playlist clips (.mlt or .kdenlive project files)
     if (clipType == AV || clipType == AUDIO || clipType == VIDEO) {
@@ -63,25 +64,51 @@ QProcess *CutClipJob::startJob(bool *ok)
         kDebug()<<"// STARTING CIUT JOS: "<<parameters;
         m_jobProcess->start("ffmpeg", parameters);
         m_jobProcess->waitForStarted();
-        QString log = m_jobProcess->readAll();
-        if (!log.isEmpty()) m_errorMessage.append(log + '\n');
-       return m_jobProcess;
+        while (m_jobProcess->state() != QProcess::NotRunning) {
+            processLogInfo();
+            if (jobStatus == JOBABORTED) {
+                m_jobProcess->close();
+                m_jobProcess->waitForFinished();
+                QFile::remove(m_dest);
+            }
+            m_jobProcess->waitForFinished(400);
+        }
+        
+        if (jobStatus != JOBABORTED) {
+            int result = m_jobProcess->exitStatus();
+            if (result == QProcess::NormalExit) {
+                if (QFileInfo(m_dest).size() == 0) {
+                    // File was not created
+                    processLogInfo();
+                    m_errorMessage.append(i18n("Failed to create file."));
+                    setStatus(JOBCRASHED);
+                }
+                else setStatus(JOBDONE);
+            }
+            else if (result == QProcess::CrashExit) {
+                // Proxy process crashed
+                QFile::remove(m_dest);
+                setStatus(JOBCRASHED);
+            }
+        }
+       delete m_jobProcess;
+        return;
     }
     else m_errorMessage = i18n("Cannot process this clip type.");
-    *ok = false;
-    return NULL;
+    setStatus(JOBCRASHED);
+    return;
 }
 
-int CutClipJob::processLogInfo()
+void CutClipJob::processLogInfo()
 {
-    if (!m_jobProcess || m_jobDuration == 0 || jobStatus == JOBABORTED) return JOBABORTED;
+    if (!m_jobProcess || m_jobDuration == 0 || jobStatus == JOBABORTED) return;
     QString log = m_jobProcess->readAll();
     if (!log.isEmpty()) m_errorMessage.append(log + '\n');
     int progress;
     // Parse FFmpeg output
     if (log.contains("frame=")) {
-        int progress = log.section("frame=", 1, 1).simplified().section(' ', 0, 0).toInt();
-        return (int) (100.0 * progress / m_jobDuration);
+        progress = log.section("frame=", 1, 1).simplified().section(' ', 0, 0).toInt();
+        emit jobProgress(m_clipId, (int) (100.0 * progress / m_jobDuration), jobType);
     }
     else if (log.contains("time=")) {
         QString time = log.section("time=", 1, 1).simplified().section(' ', 0, 0);
@@ -90,9 +117,8 @@ int CutClipJob::processLogInfo()
             progress = numbers.at(0).toInt() * 3600 + numbers.at(1).toInt() * 60 + numbers.at(2).toDouble();
         }
         else progress = (int) time.toDouble();
-        return (int) (100.0 * progress / m_jobDuration);
+        emit jobProgress(m_clipId, (int) (100.0 * progress / m_jobDuration), jobType);
     }
-    return -1;
 }
 
 CutClipJob::~CutClipJob()
@@ -128,3 +154,8 @@ const QString CutClipJob::statusMessage()
     return statusInfo;
 }
 
+bool CutClipJob::isExclusive()
+{
+    return false;
+}
+
index 16765317dde78761acaafdfc78ebf781c173afe1..e76c54cdc365db75d2a4a67e9e9956af79aed2b5 100644 (file)
@@ -35,11 +35,11 @@ public:
     CutClipJob(CLIPTYPE cType, const QString &id, QStringList parameters);
     virtual ~ CutClipJob();
     const QString destination() const;
-    QProcess *startJob(bool *ok);
+    void startJob();
     stringMap cancelProperties();
-    int processLogInfo();
-    bool addClipToProject;
+    void processLogInfo();
     const QString statusMessage();
+    bool isExclusive();
     
 private:
     QString m_dest;
diff --git a/src/projecttree/meltjob.cpp b/src/projecttree/meltjob.cpp
new file mode 100644 (file)
index 0000000..7e239e2
--- /dev/null
@@ -0,0 +1,150 @@
+/***************************************************************************
+ *                                                                         *
+ *   Copyright (C) 2011 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 "meltjob.h"
+#include "kdenlivesettings.h"
+#include "kdenlivedoc.h"
+
+#include <KDebug>
+#include <KLocale>
+
+#include <mlt++/Mlt.h>
+
+
+static void consumer_frame_render(mlt_consumer, MeltJob * self, mlt_frame /*frame_ptr*/)
+{
+    // detect if the producer has finished playing. Is there a better way to do it?
+    self->emitFrameNumber();
+}
+
+MeltJob::MeltJob(CLIPTYPE cType, const QString &id, QStringList parameters) : AbstractClipJob(MLTJOB, cType, id, parameters), 
+    m_producer(NULL),
+    m_profile(NULL),
+    m_consumer(NULL),
+    m_length(0)
+{
+    jobStatus = JOBWAITING;
+    m_params = parameters;
+    description = i18n("Process clip");
+}
+
+void MeltJob::setProducer(Mlt::Producer *producer)
+{
+    m_producer = producer;
+}
+
+void MeltJob::startJob()
+{
+    if (!m_producer) {
+        m_errorMessage.append(i18n("No producer for this clip."));
+        setStatus(JOBCRASHED);
+        return;
+    }
+    QString filter = m_params.takeFirst();
+    QString filterParams = m_params.takeFirst();
+    QString consumer = m_params.takeFirst();
+    QString consumerParams = m_params.takeFirst();
+    QString properties = m_params.takeFirst();
+    m_profile = m_producer->profile();
+    m_consumer = new Mlt::Consumer(*m_profile, consumer.toUtf8().constData());
+    if (!m_consumer || !m_consumer->is_valid()) {
+        m_errorMessage.append(i18n("Cannot create consumer %1.", consumer));
+        setStatus(JOBCRASHED);
+        return;
+    }
+
+    m_consumer->set("terminate_on_pause", 1 );
+    m_consumer->set("eof", "pause" );
+    m_consumer->set("real_time", -1 );
+
+    QStringList list = consumerParams.split(' ', QString::SkipEmptyParts);
+    foreach(QString data, list) {
+        if (data.contains('=')) {
+            m_consumer->set(data.section('=', 0, 0).toUtf8().constData(), data.section('=', 1, 1).toUtf8().constData());
+        }
+    }
+    
+    Mlt::Filter mltFilter(*m_profile, filter.toUtf8().data());
+    list = filterParams.split(' ', QString::SkipEmptyParts);
+    foreach(QString data, list) {
+        if (data.contains('=')) {
+            mltFilter.set(data.section('=', 0, 0).toUtf8().constData(), data.section('=', 1, 1).toUtf8().constData());
+        }
+    }
+    m_producer->attach(mltFilter);
+    m_length = m_producer->get_length();
+    m_consumer->connect(*m_producer);
+    m_producer->set_speed(0);
+    m_producer->seek(0);
+    m_showFrameEvent = m_consumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_render);
+    m_consumer->start();
+    m_producer->set_speed(1);
+    while (jobStatus != JOBABORTED && !m_consumer->is_stopped()) {
+        
+    }
+    m_consumer->stop();
+    QStringList wanted = properties.split(',', QString::SkipEmptyParts);
+    foreach(const QString key, wanted) {
+        QString value = mltFilter.get(key.toUtf8().constData());
+        kDebug()<<"RESULT: "<<key<<" = "<< value;
+    }
+    setStatus(JOBDONE);
+    delete m_consumer;
+    return;
+}
+
+
+MeltJob::~MeltJob()
+{
+}
+
+const QString MeltJob::destination() const
+{
+    return QString();
+}
+
+stringMap MeltJob::cancelProperties()
+{
+    QMap <QString, QString> props;
+    return props;
+}
+
+const QString MeltJob::statusMessage()
+{
+    QString statusInfo;
+    switch (jobStatus) {
+        case JOBWORKING:
+            statusInfo = i18n("Processing clip");
+            break;
+        case JOBWAITING:
+            statusInfo = i18n("Waiting - process clip");
+            break;
+        default:
+            break;
+    }
+    return statusInfo;
+}
+
+void MeltJob::emitFrameNumber()
+{
+    if (m_consumer && m_length > 0) {
+        emit jobProgress(m_clipId, (int) (100 * m_consumer->position() / m_length), jobType);
+    }
+}
\ No newline at end of file
diff --git a/src/projecttree/meltjob.h b/src/projecttree/meltjob.h
new file mode 100644 (file)
index 0000000..d2aba6f
--- /dev/null
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *                                                                         *
+ *   Copyright (C) 2011 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          *
+ ***************************************************************************/
+
+#ifndef MELTJOB
+#define MELTJOB
+
+#include <QObject>
+#include <QProcess>
+
+#include "abstractclipjob.h"
+
+namespace Mlt{
+        class Profile;
+        class Producer;
+        class Consumer;
+        class Filter;
+        class Event;
+};
+
+class MeltJob : public AbstractClipJob
+{
+    Q_OBJECT
+
+public:
+    MeltJob(CLIPTYPE cType, const QString &id, QStringList parameters);
+    virtual ~ MeltJob();
+    const QString destination() const;
+    void startJob();
+    stringMap cancelProperties();
+    bool addClipToProject;
+    const QString statusMessage();
+    void setProducer(Mlt::Producer *producer);
+    void emitFrameNumber();
+    
+private:
+    Mlt::Producer *m_producer;
+    Mlt::Profile *m_profile;
+    Mlt::Consumer *m_consumer;
+    Mlt::Event *m_showFrameEvent;
+    QStringList m_params;
+    int m_length;
+};
+
+#endif
index 36e9342a4098d2d44a48324c23d3e145b91379b4..3bd2e53cb9663b3f71f41a2e1122e840af623a4c 100644 (file)
@@ -37,9 +37,10 @@ ProxyJob::ProxyJob(CLIPTYPE cType, const QString &id, QStringList parameters) :
     m_proxyParams = parameters.at(3);
     m_renderWidth = parameters.at(4).toInt();
     m_renderHeight = parameters.at(5).toInt();
+    replaceClip = true;
 }
 
-QProcess *ProxyJob::startJob(bool *ok)
+void ProxyJob::startJob()
 {
     // Special case: playlist clips (.mlt or .kdenlive project files)
     m_jobDuration = 0;
@@ -47,41 +48,41 @@ QProcess *ProxyJob::startJob(bool *ok)
         // change FFmpeg params to MLT format
         m_isFfmpegJob = false;
         QStringList mltParameters;
-                mltParameters << m_src;
-                mltParameters << "-consumer" << "avformat:" + m_dest;
-                QStringList params = m_proxyParams.split('-', QString::SkipEmptyParts);
+        mltParameters << m_src;
+        mltParameters << "-consumer" << "avformat:" + m_dest;
+        QStringList params = m_proxyParams.split('-', QString::SkipEmptyParts);
                 
-                foreach(QString s, params) {
-                    s = s.simplified();
-                    if (s.count(' ') == 0) {
-                        s.append("=1");
-                    }
-                    else s.replace(' ', '=');
-                    mltParameters << s;
-                }
+        foreach(QString s, params) {
+            s = s.simplified();
+            if (s.count(' ') == 0) {
+                s.append("=1");
+            }
+            else s.replace(' ', '=');
+            mltParameters << s;
+        }
         
-            mltParameters.append(QString("real_time=-%1").arg(KdenliveSettings::mltthreads()));
+        mltParameters.append(QString("real_time=-%1").arg(KdenliveSettings::mltthreads()));
 
-            //TODO: currently, when rendering an xml file through melt, the display ration is lost, so we enforce it manualy
-            double display_ratio = KdenliveDoc::getDisplayRatio(m_src);
-            mltParameters << "aspect=" + QString::number(display_ratio);
+        //TODO: currently, when rendering an xml file through melt, the display ration is lost, so we enforce it manualy
+        double display_ratio = KdenliveDoc::getDisplayRatio(m_src);
+        mltParameters << "aspect=" + QString::number(display_ratio);
             
-            // Ask for progress reporting
-            mltParameters << "progress=1";
+        // Ask for progress reporting
+        mltParameters << "progress=1";
 
-           m_jobProcess = new QProcess;
-            m_jobProcess->setProcessChannelMode(QProcess::MergedChannels);
-            m_jobProcess->start(KdenliveSettings::rendererpath(), mltParameters);
-            m_jobProcess->waitForStarted();
-           return m_jobProcess;
+       m_jobProcess = new QProcess;
+        m_jobProcess->setProcessChannelMode(QProcess::MergedChannels);
+        m_jobProcess->start(KdenliveSettings::rendererpath(), mltParameters);
+        m_jobProcess->waitForStarted();
     }
     else if (clipType == IMAGE) {
         m_isFfmpegJob = false;
         // Image proxy
         QImage i(m_src);
         if (i.isNull()) {
-           *ok = false;
-           return NULL;
+           m_errorMessage.append(i18n("Cannot load image %1.", m_src));
+            setStatus(JOBCRASHED);
+           return;
        }
        
         QImage proxy;
@@ -123,8 +124,8 @@ QProcess *ProxyJob::startJob(bool *ok)
             processed.save(m_dest);
         }
         else proxy.save(m_dest);
-       *ok = true;
-       return NULL;
+        setStatus(JOBDONE);
+       return;
     }
     else {
         m_isFfmpegJob = true;
@@ -139,19 +140,48 @@ QProcess *ProxyJob::startJob(bool *ok)
         parameters << m_dest;
         m_jobProcess = new QProcess;
         m_jobProcess->setProcessChannelMode(QProcess::MergedChannels);
-        m_jobProcess->start("ffmpeg", parameters);
+        m_jobProcess->start("ffmpeg", parameters, QIODevice::ReadOnly);
         m_jobProcess->waitForStarted();
-        QString log = m_jobProcess->readAll();
-        if (!log.isEmpty()) m_errorMessage.append(log + '\n');
-       return m_jobProcess;
     }
+    while (m_jobProcess->state() != QProcess::NotRunning) {
+        processLogInfo();
+        if (jobStatus == JOBABORTED) {
+            emit cancelRunningJob(m_clipId, cancelProperties());
+            m_jobProcess->close();
+            m_jobProcess->waitForFinished();
+            QFile::remove(m_dest);
+        }
+        m_jobProcess->waitForFinished(400);
+    }
+    
+    if (jobStatus != JOBABORTED) {
+        int result = m_jobProcess->exitStatus();
+        if (result == QProcess::NormalExit) {
+            if (QFileInfo(m_dest).size() == 0) {
+                // File was not created
+                processLogInfo();
+                m_errorMessage.append(i18n("Failed to create file."));
+                setStatus(JOBCRASHED);
+            }
+            else setStatus(JOBDONE);
+        }
+        else if (result == QProcess::CrashExit) {
+            // Proxy process crashed
+            QFile::remove(m_dest);
+            setStatus(JOBCRASHED);
+        }
+    }
+    
+    delete m_jobProcess;
+    return;
 }
 
-int ProxyJob::processLogInfo()
+void ProxyJob::processLogInfo()
 {
-    if (!m_jobProcess || jobStatus == JOBABORTED) return JOBABORTED;
+    if (!m_jobProcess || jobStatus == JOBABORTED) return;
     QString log = m_jobProcess->readAll();
     if (!log.isEmpty()) m_errorMessage.append(log + '\n');
+    else return;
     int progress;
     if (m_isFfmpegJob) {
         // Parse FFmpeg output
@@ -169,17 +199,16 @@ int ProxyJob::processLogInfo()
                 progress = numbers.at(0).toInt() * 3600 + numbers.at(1).toInt() * 60 + numbers.at(2).toDouble();
             }
             else progress = (int) time.toDouble();
-            return (int) (100.0 * progress / m_jobDuration);
+            emit jobProgress(m_clipId, (int) (100.0 * progress / m_jobDuration), jobType);
         }
     }
     else {
         // Parse MLT output
         if (log.contains("percentage:")) {
             progress = log.section(':', -1).simplified().toInt();
-            return progress;
+            emit jobProgress(m_clipId, progress, jobType);
         }
     }
-    return -1;
 }
 
 ProxyJob::~ProxyJob()
index 6a524630ed9b6a85310800518338b6fde92fc10e..beca4b1e926552c03fce921801315899793a5978 100644 (file)
@@ -35,10 +35,11 @@ public:
     ProxyJob(CLIPTYPE cType, const QString &id, QStringList parameters);
     virtual ~ ProxyJob();
     const QString destination() const;
-    QProcess *startJob(bool *ok);
+    void startJob();
     stringMap cancelProperties();
-    int processLogInfo();
     const QString statusMessage();
+    void processLogInfo();
+
     
 private:
     QString m_dest;
index eba43211e2bfd7067ddaf9cacb33bf7075b9c295..4c4f0a4b5e935a2ce967d1368a1b9ab024df0043 100644 (file)
@@ -67,6 +67,7 @@ public:
             painter->drawText(r1, Qt::AlignLeft | Qt::AlignTop , index.data(Qt::UserRole).toString());
             int progress = index.data(Qt::UserRole + 3).toInt();
             if (progress > 0 && progress < 100) {
+                // draw progress bar
                 QColor color = option.palette.alternateBase().color();
                 QColor fgColor = option.palette.text().color();
                 color.setAlpha(150);
@@ -75,7 +76,7 @@ public:
                 painter->setPen(QPen(fgColor));
                 int width = qMin(200, r1.width() - 4);
                 QRect bgrect(r1.left() + 2, option.rect.bottom() - 6 - textMargin, width, 6);
-                painter->drawRect(bgrect);
+                painter->drawRoundedRect(bgrect, 3, 3);
                 painter->setBrush(QBrush(fgColor));
                 bgrect.adjust(2, 2, 0, -1);
                 painter->setPen(Qt::NoPen);