From 1705785a526c00d52ea41f271e52d4bca9ab8650 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Mon, 19 Dec 2011 23:32:00 +0100 Subject: [PATCH] Continue progress on clip jobs: add extract zone action (cuts a clip through ffmpeg codec copy) --- src/CMakeLists.txt | 1 + src/clipmanager.cpp | 4 +- src/clipproperties.cpp | 8 ++ src/mainwindow.cpp | 2 + src/monitor.cpp | 8 ++ src/monitor.h | 3 + src/projectitem.cpp | 32 ++++-- src/projectitem.h | 8 +- src/projectlist.cpp | 165 +++++++++++++++++++++++------- src/projectlist.h | 23 +++-- src/projecttree/CMakeLists.txt | 1 + src/projecttree/abstractclipjob.h | 2 +- src/projecttree/cutclipjob.cpp | 106 +++++++++++++++++++ src/projecttree/cutclipjob.h | 51 +++++++++ src/projecttree/proxyclipjob.cpp | 6 +- src/projecttree/proxyclipjob.h | 2 +- src/renderer.cpp | 3 + src/timecode.cpp | 7 +- src/timecode.h | 2 +- src/widgets/cutjobdialog_ui.ui | 143 ++++++++++++++++++++++++++ 20 files changed, 516 insertions(+), 61 deletions(-) create mode 100644 src/projecttree/cutclipjob.cpp create mode 100644 src/projecttree/cutclipjob.h create mode 100644 src/widgets/cutjobdialog_ui.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f957281..351157de 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -256,6 +256,7 @@ kde4_add_ui_files(kdenlive_UIS widgets/wizardstandard_ui.ui widgets/keywordval_ui.ui widgets/fontval_ui.ui + widgets/cutjobdialog_ui.ui ) if(OPENGL_FOUND) diff --git a/src/clipmanager.cpp b/src/clipmanager.cpp index dcd935a2..160d3375 100644 --- a/src/clipmanager.cpp +++ b/src/clipmanager.cpp @@ -463,9 +463,8 @@ void ClipManager::resetProducersList(const QList prods, bool d void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, const QString &groupId) { QUndoCommand *addClips = new QUndoCommand(); - foreach(const KUrl & file, urls) { - if (KIO::NetAccess::exists(file, KIO::NetAccess::SourceSide, NULL)) { + if (QFile::exists(file.path())) {//KIO::NetAccess::exists(file, KIO::NetAccess::SourceSide, NULL)) { if (!getClipByResource(file.path()).empty()) { if (KMessageBox::warningContinueCancel(kapp->activeWindow(), i18n("Clip %1
already exists in project, what do you want to do?", file.path()), i18n("Clip already exists")) == KMessageBox::Cancel) continue; @@ -529,6 +528,7 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c } new AddClipCommand(m_doc, doc.documentElement(), QString::number(id), true, addClips); } + else kDebug()<<"// CANNOT READ FILE: "<childCount() > 0) { addClips->setText(i18np("Add clip", "Add clips", addClips->childCount())); diff --git a/src/clipproperties.cpp b/src/clipproperties.cpp index 42214833..db4c3bc8 100644 --- a/src/clipproperties.cpp +++ b/src/clipproperties.cpp @@ -401,6 +401,13 @@ ClipProperties::ClipProperties(DocClipBase *clip, Timecode tc, double fps, QWidg new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Frame rate") << props.value("fps")); if (!m_view.clip_framerate->isEnabled()) m_view.clip_framerate->setValue(props.value("fps").toDouble()); } + + if (props.contains("progressive")) { + int scanning = props.value("progressive").toInt(); + QString txt = scanning == 1 ? i18n("Progressive") : i18n("Interlaced"); + new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Scanning") << txt); + } + if (props.contains("aspect_ratio")) new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Pixel aspect ratio") << props.value("aspect_ratio")); @@ -409,6 +416,7 @@ ClipProperties::ClipProperties(DocClipBase *clip, Timecode tc, double fps, QWidg if (props.contains("colorspace")) new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Colorspace") << ProfilesDialog::getColorspaceDescription(props.value("colorspace").toInt())); + int width = 180.0 * KdenliveSettings::project_display_ratio(); if (width % 2 == 1) width++; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a090bf03..6aedb516 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -232,6 +232,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & connect(m_projectList, SIGNAL(refreshClip(const QString &, bool)), m_monitorManager, SLOT(slotRefreshCurrentMonitor(const QString &))); connect(m_projectList, SIGNAL(findInTimeline(const QString&)), this, SLOT(slotClipInTimeline(const QString&))); connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint))); + connect(m_clipMonitor, SIGNAL(extractZone(const QString &, QPoint)), m_projectList, SLOT(slotCutClipJob(const QString &, QPoint))); m_projectMonitorDock = new QDockWidget(i18n("Project Monitor"), this); m_projectMonitorDock->setObjectName("project_monitor"); @@ -4477,6 +4478,7 @@ void MainWindow::slotElapsedTime() kDebug()<<"-----------------------------------------\n"<<"Time elapsed: "<addMenu(m_markerMenu); m_contextMenu->addAction(KIcon("document-save"), i18n("Save zone"), this, SLOT(slotSaveZone())); + QAction *extractZone = m_configMenu->addAction(KIcon("document-new"), i18n("Extract Zone"), this, SLOT(slotExtractCurrentZone())); + m_contextMenu->addAction(extractZone); } QAction *extractFrame = m_configMenu->addAction(KIcon("document-new"), i18n("Extract frame"), this, SLOT(slotExtractCurrentFrame())); m_contextMenu->addAction(extractFrame); @@ -566,6 +568,12 @@ void Monitor::slotSetThumbFrame() emit refreshClipThumbnail(m_currentClip->getId(), true); } +void Monitor::slotExtractCurrentZone() +{ + if (m_currentClip == NULL) return; + emit extractZone(m_currentClip->getId(), m_ruler->zone()); +} + void Monitor::slotExtractCurrentFrame() { QImage frame; diff --git a/src/monitor.h b/src/monitor.h index 04e5af2d..518ba072 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -199,6 +199,7 @@ private slots: void slotSetVolume(int volume); void slotShowVolume(); void slotEditMarker(); + void slotExtractCurrentZone(); public slots: void slotOpenFile(const QString &); @@ -252,6 +253,8 @@ signals: /** @brief Editing transitions / effects over the monitor requires the renderer to send frames as QImage. * This causes a major slowdown, so we only enable it if required */ void requestFrameForAnalysis(bool); + /** @brief Request a zone extraction (ffmpeg transcoding). */ + void extractZone(const QString &id, QPoint zone); }; #endif diff --git a/src/projectitem.cpp b/src/projectitem.cpp index 469d7e29..43f91e9b 100644 --- a/src/projectitem.cpp +++ b/src/projectitem.cpp @@ -30,7 +30,9 @@ #include const int DurationRole = Qt::UserRole + 1; -const int ProxyRole = Qt::UserRole + 5; +const int JobProgressRole = Qt::UserRole + 5; +const int JobTypeRole = Qt::UserRole + 6; +const int JobCrasMessage = Qt::UserRole + 7; const int itemHeight = 38; ProjectItem::ProjectItem(QTreeWidget * parent, DocClipBase *clip) : @@ -167,7 +169,7 @@ void ProjectItem::slotSetToolTip() { QString tip; if (m_clip->isPlaceHolder()) tip.append(i18n("Missing") + " | "); - int s = data(0, ProxyRole).toInt(); + int s = data(0, JobProgressRole).toInt(); if (s == CREATINGJOB || s > 0) { tip.append(i18n("Building proxy clip") + " | "); } @@ -263,11 +265,16 @@ void ProjectItem::setProperties(const QMap < QString, QString > &attributes, con } } -void ProjectItem::setProxyStatus(CLIPJOBSTATUS status, int progress) +void ProjectItem::setJobStatus(CLIPJOBSTATUS status, int progress, JOBTYPE jobType) { - if (progress > 0) setData(0, ProxyRole, progress); + if (status == JOBCRASHED) { + if (jobType == PROXYJOB) setData(0, JobCrasMessage, i18n("Proxy crashed")); + else if (jobType == CUTJOB) setData(0, JobCrasMessage, i18n("Transcoding crashed")); + } + setData(0, JobTypeRole, jobType); + if (progress > 0) setData(0, JobProgressRole, progress); else { - setData(0, ProxyRole, status); + setData(0, JobProgressRole, status); slotSetToolTip(); } } @@ -275,22 +282,29 @@ void ProjectItem::setProxyStatus(CLIPJOBSTATUS status, int progress) bool ProjectItem::hasProxy() const { if (m_clip == NULL) return false; - if (m_clip->getProperty("proxy").isEmpty() || m_clip->getProperty("proxy") == "-" || data(0, ProxyRole).toInt() == JOBCRASHED) return false; + if (m_clip->getProperty("proxy").isEmpty() || m_clip->getProperty("proxy") == "-" || data(0, JobProgressRole).toInt() == JOBCRASHED) return false; return true; } bool ProjectItem::isProxyReady() const { - return (data(0, ProxyRole).toInt() == JOBDONE); + return (data(0, JobProgressRole).toInt() == JOBDONE); } -bool ProjectItem::isProxyRunning() const +bool ProjectItem::isJobRunning() const { - int s = data(0, ProxyRole).toInt(); + int s = data(0, JobProgressRole).toInt(); if (s == JOBWAITING || s == CREATINGJOB || s > 0) return true; return false; } +bool ProjectItem::isProxyRunning() const +{ + int s = data(0, JobProgressRole).toInt(); + if ((s == CREATINGJOB || s > 0) && data(0, JobTypeRole).toInt() == (int) PROXYJOB) return true; + return false; +} + bool ProjectItem::playlistHasProxies(const QString path) { kDebug()<<"// CHECKING FOR PROXIES"; diff --git a/src/projectitem.h b/src/projectitem.h index 4bdf653a..06a6802e 100644 --- a/src/projectitem.h +++ b/src/projectitem.h @@ -30,6 +30,8 @@ #include "gentime.h" #include "definitions.h" +#include "projecttree/abstractclipjob.h" + class DocClipBase; @@ -63,12 +65,14 @@ public: QString getClipHash() const; static int itemDefaultHeight(); void slotSetToolTip(); - /** \brief Set the status of proxy clip creation. 0 = no proxy, 1 = creating proxy, 2 = proxy created. */ - void setProxyStatus(CLIPJOBSTATUS status, int progress = 0); + /** \brief Set the status of the clip job. */ + void setJobStatus(CLIPJOBSTATUS status, int progress = 0, JOBTYPE jobType = NOJOBTYPE); /** \brief Returns the proxy status for this clip (true means there is a proxy clip). */ bool hasProxy() const; /** \brief Returns true if the proxy for this clip is ready. */ bool isProxyReady() const; + /** \brief Returns true if there is a job currently running for this clip. */ + bool isJobRunning() const; /** \brief Returns true if we are currently creating the proxy for this clip. */ bool isProxyRunning() const; diff --git a/src/projectlist.cpp b/src/projectlist.cpp index 38391bd2..174e5ff2 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -21,6 +21,7 @@ #include "projectitem.h" #include "commands/addfoldercommand.h" #include "projecttree/proxyclipjob.h" +#include "projecttree/cutclipjob.h" #include "kdenlivesettings.h" #include "slideshowclip.h" #include "ui_colorclip_ui.h" @@ -40,6 +41,7 @@ #include "commands/addclipcutcommand.h" #include "ui_templateclip_ui.h" +#include "ui_cutjobdialog_ui.h" #include #include @@ -52,6 +54,7 @@ #include #include #include +#include #ifdef NEPOMUK #include @@ -268,6 +271,7 @@ ProjectList::ProjectList(QWidget *parent) : connect(m_listView, SIGNAL(requestMenu(const QPoint &, QTreeWidgetItem *)), this, SLOT(slotContextMenu(const QPoint &, QTreeWidgetItem *))); connect(m_listView, SIGNAL(addClip()), this, SLOT(slotAddClip())); connect(m_listView, SIGNAL(addClip(const QList , const QString &, const QString &)), this, SLOT(slotAddClip(const QList , const QString &, const QString &))); + connect(this, SIGNAL(addClip(const QString, const QString &, const QString &)), this, SLOT(slotAddClip(const QString, const QString &, const QString &))); connect(m_listView, SIGNAL(addClipCut(const QString &, int, int)), this, SLOT(slotAddClipCut(const QString &, int, int))); connect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int))); connect(m_listView, SIGNAL(showProperties(DocClipBase *)), this, SIGNAL(showClipProperties(DocClipBase *))); @@ -627,7 +631,7 @@ void ProjectList::slotReloadClip(const QString &id) continue; } item = static_cast (selected.at(i)); - if (item && !item->isProxyRunning()) { + if (item && !hasPendingProxy(item)) { DocClipBase *clip = item->referencedClip(); if (!clip || !clip->isClean() || m_render->isProcessing(item->clipId())) { kDebug()<<"//// TRYING TO RELOAD: "<clipId()<<", but it is busy"; @@ -893,7 +897,7 @@ void ProjectList::slotUpdateClipProperties(ProjectItem *clip, QMap setProperties(properties); if (properties.contains("proxy")) { if (properties.value("proxy") == "-" || properties.value("proxy").isEmpty()) - clip->setProxyStatus(NOJOB); + clip->setJobStatus(NOJOB); } if (properties.contains("name")) { monitorItemEditing(false); @@ -1114,6 +1118,7 @@ void ProjectList::slotDeleteClip(const QString &clipId) kDebug() << "/// Cannot find clip to delete"; return; } + deleteJobsForClip(clipId); if (item->isProxyRunning()) m_abortProxy.append(item->referencedClip()->getProperty("proxy")); m_listView->blockSignals(true); QTreeWidgetItem *newSelectedItem = m_listView->itemAbove(item); @@ -1241,7 +1246,7 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) m_render->getFileProperties(e, clip->getId(), m_listView->iconSize().height(), true); } // WARNING: code below triggers unnecessary reload of all proxy clips on document loading... is it useful in some cases? - /*else if (item->hasProxy() && !item->isProxyRunning()) { + /*else if (item->hasProxy() && !item->isJobRunning()) { slotCreateProxy(clip->getId()); }*/ @@ -1439,11 +1444,11 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged, QStr bool replace = false; if (brokenClips.contains(item->clipId())) { // if this is a proxy clip, disable proxy - item->setProxyStatus(NOJOB); + item->setJobStatus(NOJOB); clip->setProperty("proxy", "-"); replace = true; } - if (clip->isPlaceHolder() == false && !item->isProxyRunning()) { + if (clip->isPlaceHolder() == false && !hasPendingProxy(item)) { QDomElement xml = clip->toXML(); if (fpsChanged) { xml.removeAttribute("out"); @@ -1524,6 +1529,14 @@ QString ProjectList::getExtensions() return allExtensions.simplified(); } +void ProjectList::slotAddClip(const QString url, const QString &groupName, const QString &groupId) +{ + kDebug()<<"// Adding clip: "< list; + list.append(url); + slotAddClip(list, groupName, groupId); +} + void ProjectList::slotAddClip(const QList givenList, const QString &groupName, const QString &groupId) { if (!m_commandStack) @@ -1651,7 +1664,7 @@ void ProjectList::slotRemoveInvalidProxy(const QString &id, bool durationError) kDebug() << "Proxy duration is wrong, try changing transcoding parameters."; emit displayMessage(i18n("Proxy clip unusable (duration is different from original)."), -2); } - item->setProxyStatus(JOBCRASHED); + item->setJobStatus(JOBCRASHED); QString path = item->referencedClip()->getProperty("proxy"); KUrl proxyFolder(m_doc->projectFolder().path( KUrl::AddTrailingSlash) + "proxy/"); @@ -1983,9 +1996,9 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce // Proxy stuff QString size = properties.value("frame_size"); - if (!useProxy() && clip->getProperty("proxy").isEmpty()) setProxyStatus(item, NOJOB); - if (useProxy() && generateProxy() && clip->getProperty("proxy") == "-") setProxyStatus(item, NOJOB); - else if (useProxy() && !item->hasProxy() && !item->isProxyRunning()) { + if (!useProxy() && clip->getProperty("proxy").isEmpty()) setJobStatus(item, NOJOB); + if (useProxy() && generateProxy() && clip->getProperty("proxy") == "-") setJobStatus(item, NOJOB); + else if (useProxy() && !item->hasProxy() && !hasPendingProxy(item)) { // proxy video and image clips int maxSize; CLIPTYPE t = item->clipType(); @@ -2474,13 +2487,13 @@ QMap ProjectList::getProxies() void ProjectList::slotCreateProxy(const QString id) { ProjectItem *item = getItemById(id); - if (!item || item->isProxyRunning() || item->referencedClip()->isPlaceHolder()) return; + if (!item || hasPendingProxy(item) || item->referencedClip()->isPlaceHolder()) return; QString path = item->referencedClip()->getProperty("proxy"); if (path.isEmpty()) { - setProxyStatus(item, JOBCRASHED); + setJobStatus(item, JOBCRASHED); return; } - setProxyStatus(item, JOBWAITING); + if (m_abortProxy.contains(path)) m_abortProxy.removeAll(path); if (m_processingProxy.contains(path)) { // Proxy is already being generated @@ -2488,19 +2501,70 @@ void ProjectList::slotCreateProxy(const QString id) } if (QFileInfo(path).size() > 0) { // Proxy already created - setProxyStatus(item, JOBDONE); + setJobStatus(item, JOBDONE); slotGotProxy(path); return; } + if (!item->isJobRunning()) setJobStatus(item, JOBWAITING); m_processingProxy.append(path); - /*PROXYINFO info; - info.dest = path; - info.src = item->clipUrl().path(); - info.type = item->clipType(); - info.exif = QString(item->referencedClip()->producerProperty("_exif_orientation")).toInt();*/ - ProxyJob *job = new ProxyJob(PROXYJOB, item->clipType(), item->clipId(), 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())); + 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); + + startJobProcess(); +} + +void ProjectList::slotCutClipJob(const QString &id, QPoint zone) +{ + ProjectItem *item = getItemById(id); + if (!item|| item->referencedClip()->isPlaceHolder()) return; + QString dest = item->clipUrl().path(); + QString ext = dest.section('.', -1); + dest = dest.section('.', 0, -2) + "_" + QString::number(zone.x()) + "." + ext; + + double clipFps = item->referencedClip()->getProperty("fps").toDouble(); + if (clipFps == 0) clipFps = m_fps; + // if clip and project have different frame rate, adjust in and out + int in = zone.x(); + int out = zone.y(); + in = GenTime(in, m_timecode.fps()).frames(clipFps); + out = GenTime(out, m_timecode.fps()).frames(clipFps); + int max = GenTime(item->clipMaxDuration(), m_timecode.fps()).frames(clipFps); + int duration = out - in; + QString timeIn = Timecode::getStringTimecode(in, clipFps, true); + QString timeOut = Timecode::getStringTimecode(duration, clipFps, true); + + QDialog *d = new QDialog(this); + Ui::CutJobDialog_UI ui; + ui.setupUi(d); + ui.file_url->fileDialog()->setOperationMode(KFileDialog::Saving); + ui.file_url->setUrl(KUrl(dest)); + ui.info_label->setText(i18n("Extracting %1 out of %2", timeOut, Timecode::getStringTimecode(max, clipFps, true))); + if (d->exec() != QDialog::Accepted) return; + dest = ui.file_url->url().path(); + QString extraParams = ui.extra_params->toPlainText().simplified(); + delete d; + + if (QFileInfo(dest).size() > 0) { + // Clip already created + setJobStatus(item, JOBDONE); + emit addClip(dest, QString(), QString()); + return; + } + if (!item->isJobRunning()) setJobStatus(item, JOBWAITING); + m_processingProxy.append(dest); + QStringList jobParams; + jobParams << dest << item->clipUrl().path() << timeIn << timeOut << QString::number(duration); + if (!extraParams.isEmpty()) jobParams << extraParams; + CutClipJob *job = new CutClipJob(item->clipType(), id, jobParams); m_jobList.append(job); + + startJobProcess(); +} + + +void ProjectList::startJobProcess() +{ int ct = 0; if (!m_proxyThreads.futures().isEmpty()) { // Remove inactive threads @@ -2514,7 +2578,7 @@ void ProjectList::slotCreateProxy(const QString id) } emit jobCount(ct + m_jobList.count()); - if (m_proxyThreads.futures().isEmpty() || m_proxyThreads.futures().count() < KdenliveSettings::proxythreads()) m_proxyThreads.addFuture(QtConcurrent::run(this, &ProjectList::slotGenerateProxy)); + if (m_proxyThreads.futures().isEmpty() || m_proxyThreads.futures().count() < KdenliveSettings::proxythreads()) m_proxyThreads.addFuture(QtConcurrent::run(this, &ProjectList::slotProcessJobs)); } void ProjectList::slotAbortProxy(const QString id, const QString path) @@ -2531,7 +2595,7 @@ void ProjectList::slotAbortProxy(const QString id, const QString path) } } -void ProjectList::slotGenerateProxy() +void ProjectList::slotProcessJobs() { while (!m_jobList.isEmpty() && !m_abortAllJobs) { emit projectModified(); @@ -2577,7 +2641,7 @@ void ProjectList::slotGenerateProxy() QFile file(job->destination()); if (!file.open(QIODevice::WriteOnly)) { for (int i = 0; i < processingItems.count(); i++) - setProxyStatus(processingItems.at(i), JOBCRASHED); + setJobStatus(processingItems.at(i), JOBCRASHED); m_processingProxy.removeAll(job->destination()); delete job; continue; @@ -2586,7 +2650,8 @@ void ProjectList::slotGenerateProxy() QFile::remove(job->destination()); for (int i = 0; i < processingItems.count(); i++) - setProxyStatus(processingItems.at(i), CREATINGJOB, 0); //, job->description); + setJobStatus(processingItems.at(i), CREATINGJOB, 0, job->jobType); + bool success; QProcess *jobProcess = job->startJob(&success); @@ -2594,9 +2659,11 @@ void ProjectList::slotGenerateProxy() if (jobProcess == NULL) { // job is finished for (int i = 0; i < processingItems.count(); i++) - setProxyStatus(processingItems.at(i), success ? JOBDONE : JOBCRASHED); + setJobStatus(processingItems.at(i), success ? JOBDONE : JOBCRASHED); if (success) { - slotGotProxy(job->destination()); + if (job->jobType == PROXYJOB) slotGotProxy(job->destination()); + //TODO: set folder for transcoded clips + else if (job->jobType == CUTJOB) emit addClip(job->destination(), QString(), QString()); } else { QFile::remove(job->destination()); @@ -2617,14 +2684,14 @@ void ProjectList::slotGenerateProxy() if (!m_closing) { emit cancelRunningJob(job->clipId(), job->cancelProperties()); /*for (int i = 0; i < processingItems.count(); i++) - setProxyStatus(processingItems.at(i), NOJOB);*/ + setJobStatus(processingItems.at(i), NOJOB);*/ } else continue; result = -2; } else { int progress = job->processLogInfo(); - if (progress > -1) processLogInfo(processingItems, progress); + if (progress > -1) processLogInfo(processingItems, progress, job->jobType); } jobProcess->waitForFinished(500); } @@ -2639,14 +2706,16 @@ void ProjectList::slotGenerateProxy() if (result == QProcess::NormalExit) { // proxy successfully created for (int i = 0; i < processingItems.count(); i++) - setProxyStatus(processingItems.at(i), JOBDONE); - slotGotProxy(job->destination()); + setJobStatus(processingItems.at(i), JOBDONE); + if (job->jobType == PROXYJOB) slotGotProxy(job->destination()); + //TODO: set folder for transcoded clips + else if (job->jobType == CUTJOB) emit addClip(job->destination(), QString(), QString()); } else if (result == QProcess::CrashExit) { // Proxy process crashed QFile::remove(job->destination()); for (int i = 0; i < processingItems.count(); i++) - setProxyStatus(processingItems.at(i), JOBCRASHED); + setJobStatus(processingItems.at(i), JOBCRASHED); } delete job; continue; @@ -2662,10 +2731,10 @@ void ProjectList::slotGenerateProxy() } -void ProjectList::processLogInfo(QList items, int progress) +void ProjectList::processLogInfo(QList items, int progress, JOBTYPE jobType) { for (int i = 0; i < items.count(); i++) - setProxyStatus(items.at(i), CREATINGJOB, progress); + setJobStatus(items.at(i), CREATINGJOB, progress, jobType); } void ProjectList::updateProxyConfig() @@ -2687,7 +2756,7 @@ void ProjectList::updateProxyConfig() } CLIPTYPE t = item->clipType(); if ((t == VIDEO || t == AV || t == UNKNOWN) && item->referencedClip() != NULL) { - if (generateProxy() && useProxy() && !item->isProxyRunning()) { + if (generateProxy() && useProxy() && !hasPendingProxy(item)) { DocClipBase *clip = item->referencedClip(); if (clip->getProperty("frame_size").section('x', 0, 0).toInt() > m_doc->getDocumentProperty("proxyminsize").toInt()) { if (clip->getProperty("proxy").isEmpty()) { @@ -2846,11 +2915,11 @@ void ProjectList::slotDeleteProxy(const QString proxyPath) QFile::remove(proxyPath); } -void ProjectList::setProxyStatus(ProjectItem *item, CLIPJOBSTATUS status, int progress) +void ProjectList::setJobStatus(ProjectItem *item, CLIPJOBSTATUS status, int progress, JOBTYPE jobType) { if (item == NULL || m_abortAllJobs) return; monitorItemEditing(false); - item->setProxyStatus(status, progress); + item->setJobStatus(status, progress, jobType); if (status == JOBCRASHED) { DocClipBase *clip = item->referencedClip(); if (!clip) { @@ -2949,4 +3018,30 @@ void ProjectList::slotCancelRunningJob(const QString id, stringMap newProps) m_commandStack->push(command); } +bool ProjectList::hasPendingProxy(ProjectItem *item) +{ + if (!item || !item->referencedClip() || m_abortAllJobs) return false; + for (int i = 0; i < m_jobList.count(); i++) { + if (m_abortAllJobs) break; + if (m_jobList.at(i)->clipId() == item->clipId() && m_jobList.at(i)->jobType == PROXYJOB) return true; + } + if (item->isProxyRunning()) return true; + return false; +} + +void ProjectList::deleteJobsForClip(const QString &clipId) +{ + QList jobsToDelete; + for (int i = 0; i < m_jobList.count(); i++) { + if (m_abortAllJobs) break; + if (m_jobList.at(i)->clipId() == clipId) { + AbstractClipJob *job = m_jobList.takeAt(i); + delete job; + i--; + } + } +} + + + #include "projectlist.moc" diff --git a/src/projectlist.h b/src/projectlist.h index e273b724..a2ee232a 100644 --- a/src/projectlist.h +++ b/src/projectlist.h @@ -51,6 +51,7 @@ #include "kdenlivesettings.h" #include "folderprojectitem.h" #include "subprojectitem.h" +#include "projecttree/abstractclipjob.h" #include namespace Mlt @@ -170,7 +171,7 @@ public: } } else if (proxy == JOBCRASHED) { - proxyText = i18n("Proxy crashed"); + proxyText = index.data(Qt::UserRole + 7).toString(); QRectF txtBounding = painter->boundingRect(r2, Qt::AlignRight | Qt::AlignVCenter, " " + proxyText + " "); painter->setPen(Qt::NoPen); painter->setBrush(option.palette.highlight()); @@ -269,6 +270,7 @@ public slots: /** @brief Prepares removing the selected items. */ void slotRemoveClip(); + void slotAddClip(const QString url, const QString &groupName, const QString &groupId); void slotAddClip(const QList givenList = QList (), const QString &groupName = QString(), const QString &groupId = QString()); /** @brief Adds, edits or deletes a folder item. @@ -288,6 +290,8 @@ public slots: void slotForceProcessing(const QString &id); /** @brief Remove all instances of a proxy and delete the file. */ void slotDeleteProxy(const QString proxyPath); + /** @brief Start a hard cut clip job. */ + void slotCutClipJob(const QString &id, QPoint zone); private: ProjectListView *m_listView; @@ -354,16 +358,22 @@ private: /** @brief Set the Proxy status on a clip. * @param item The clip item to set status - * @param status The proxy status (see definitions.h) */ - void setProxyStatus(ProjectItem *item, CLIPJOBSTATUS status, int progress = 0); + * @param status The job status (see definitions.h) */ + void setJobStatus(ProjectItem *item, CLIPJOBSTATUS status, int progress = 0, JOBTYPE jobType = NOJOBTYPE); /** @brief Process ffmpeg output to find out process progress. */ - void processLogInfo(QList items, int progress); + void processLogInfo(QList items, int progress, JOBTYPE jobType); void monitorItemEditing(bool enable); /** @brief Get cached thumbnail for a project's clip or create it if no cache. */ void getCachedThumbnail(ProjectItem *item); 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 startJobProcess(); + /** @brief Check if a clip has a running or pending proxy process. */ + bool hasPendingProxy(ProjectItem *item); + /** @brief Delete pending jobs for a clip. */ + void deleteJobsForClip(const QString &clipId); private slots: void slotClipSelected(); @@ -402,8 +412,8 @@ private slots: void slotCreateProxy(const QString id); /** @brief Stop creation of this clip's proxy. */ void slotAbortProxy(const QString id, const QString path); - /** @brief Start creation of proxy clip. */ - void slotGenerateProxy(); + /** @brief Start creation of clip jobs. */ + void slotProcessJobs(); /** @brief Discard running and pending clip jobs. */ void slotCancelJobs(); /** @brief Discard a running clip jobs. */ @@ -434,6 +444,7 @@ signals: /** @brief Set number of running jobs. */ void jobCount(int); void cancelRunningJob(const QString, stringMap); + void addClip(const QString, const QString &, const QString &); }; #endif diff --git a/src/projecttree/CMakeLists.txt b/src/projecttree/CMakeLists.txt index 8cac2b7e..f05f9fcc 100644 --- a/src/projecttree/CMakeLists.txt +++ b/src/projecttree/CMakeLists.txt @@ -2,5 +2,6 @@ set(kdenlive_SRCS ${kdenlive_SRCS} projecttree/abstractclipjob.cpp projecttree/proxyclipjob.cpp + projecttree/cutclipjob.cpp PARENT_SCOPE ) diff --git a/src/projecttree/abstractclipjob.h b/src/projecttree/abstractclipjob.h index 74c656ca..6975b476 100644 --- a/src/projecttree/abstractclipjob.h +++ b/src/projecttree/abstractclipjob.h @@ -26,7 +26,7 @@ #include "definitions.h" -enum JOBTYPE { PROXYJOB = 1, CUTJOB = 2}; +enum JOBTYPE { NOJOBTYPE = 0, PROXYJOB = 1, CUTJOB = 2}; class AbstractClipJob : public QObject { diff --git a/src/projecttree/cutclipjob.cpp b/src/projecttree/cutclipjob.cpp new file mode 100644 index 00000000..9397e2b8 --- /dev/null +++ b/src/projecttree/cutclipjob.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * * + * 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 "cutclipjob.h" +#include "kdenlivesettings.h" +#include "kdenlivedoc.h" + +#include +#include + +CutClipJob::CutClipJob(CLIPTYPE cType, const QString &id, QStringList parameters) : AbstractClipJob(CUTJOB, cType, id, parameters) +{ + description = i18n("clip cut"); + m_dest = parameters.at(0); + m_src = parameters.at(1); + m_start = parameters.at(2); + m_end = parameters.at(3); + m_jobDuration = parameters.at(4).toInt(); + if (parameters.count() == 6) m_cutExtraParams = parameters.at(5).simplified(); +} + +QProcess *CutClipJob::startJob(bool *ok) +{ + // Special case: playlist clips (.mlt or .kdenlive project files) + if (clipType == AV || clipType == AUDIO || clipType == VIDEO) { + QStringList parameters; + parameters << "-i" << m_src; + parameters << "-ss" << m_start <<"-t" << m_end; + parameters << "-acodec" << "copy" << "-vcodec" << "copy"; + if (!m_cutExtraParams.isEmpty()) { + foreach(const QString &s, m_cutExtraParams.split(' ')) + parameters << s; + } + + // Make sure we don't block when proxy file already exists + parameters << "-y"; + parameters << m_dest; + m_jobProcess = new QProcess; + m_jobProcess->setProcessChannelMode(QProcess::MergedChannels); + kDebug()<<"// STARTING CIUT JOS: "<start("ffmpeg", parameters); + m_jobProcess->waitForStarted(); + return m_jobProcess; + } + *ok = false; + return NULL; +} + +int CutClipJob::processLogInfo() +{ + if (!m_jobProcess || m_jobDuration == 0) return -1; + QString log = m_jobProcess->readAll(); + kDebug()< props; + return props; +} + + diff --git a/src/projecttree/cutclipjob.h b/src/projecttree/cutclipjob.h new file mode 100644 index 00000000..c6178cf9 --- /dev/null +++ b/src/projecttree/cutclipjob.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * * + * 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 CUTCLIPJOB +#define CUTCLIPJOB + +#include +#include + +#include "abstractclipjob.h" + + +class CutClipJob : public AbstractClipJob +{ + Q_OBJECT + +public: + CutClipJob(CLIPTYPE cType, const QString &id, QStringList parameters); + virtual ~ CutClipJob(); + const QString destination() const; + QProcess *startJob(bool *ok); + stringMap cancelProperties(); + int processLogInfo(); + +private: + QString m_dest; + QString m_src; + QString m_start; + QString m_end; + QString m_cutExtraParams; + int m_jobDuration; +}; + +#endif diff --git a/src/projecttree/proxyclipjob.cpp b/src/projecttree/proxyclipjob.cpp index 5526eb2b..9ff69087 100644 --- a/src/projecttree/proxyclipjob.cpp +++ b/src/projecttree/proxyclipjob.cpp @@ -25,7 +25,7 @@ #include #include -ProxyJob::ProxyJob(JOBTYPE type, CLIPTYPE cType, const QString &id, QStringList parameters) : AbstractClipJob(type, cType, id, parameters), +ProxyJob::ProxyJob(CLIPTYPE cType, const QString &id, QStringList parameters) : AbstractClipJob(PROXYJOB, cType, id, parameters), m_jobDuration(0), m_isFfmpegJob(true) { @@ -130,8 +130,8 @@ QProcess *ProxyJob::startJob(bool *ok) QStringList parameters; parameters << "-i" << m_src; QString params = m_proxyParams; - foreach(QString s, params.split(' ')) - parameters << s; + foreach(const QString &s, params.split(' ')) + parameters << s; // Make sure we don't block when proxy file already exists parameters << "-y"; diff --git a/src/projecttree/proxyclipjob.h b/src/projecttree/proxyclipjob.h index 82366aff..20c04d3f 100644 --- a/src/projecttree/proxyclipjob.h +++ b/src/projecttree/proxyclipjob.h @@ -32,7 +32,7 @@ class ProxyJob : public AbstractClipJob Q_OBJECT public: - ProxyJob(JOBTYPE type, CLIPTYPE cType, const QString &id, QStringList parameters); + ProxyJob(CLIPTYPE cType, const QString &id, QStringList parameters); virtual ~ ProxyJob(); const QString destination() const; QProcess *startJob(bool *ok); diff --git a/src/renderer.cpp b/src/renderer.cpp index a0ff4312..17f26470 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -915,6 +915,9 @@ void Render::processFileProperties() int video_max = 0; int default_audio = producer->get_int("audio_index"); int audio_max = 0; + + int scan = producer->get_int("meta.media.progressive"); + filePropertyMap["progressive"] = QString::number(scan); // Find maximum stream index values for (int ix = 0; ix < producer->get_int("meta.media.nb_streams"); ix++) { diff --git a/src/timecode.cpp b/src/timecode.cpp index ca51d7cf..08d524d1 100644 --- a/src/timecode.cpp +++ b/src/timecode.cpp @@ -210,7 +210,7 @@ const QString Timecode::getTimecodeFromFrames(int frames) const //static -QString Timecode::getStringTimecode(int frames, const double &fps) +QString Timecode::getStringTimecode(int frames, const double &fps, bool showFrames) { // Returns the timecode in an hh:mm:ss format @@ -221,6 +221,7 @@ QString Timecode::getStringTimecode(int frames, const double &fps) } int seconds = (int)(frames / fps); + int frms = frames % (int) (fps + 0.5); int minutes = seconds / 60; seconds = seconds % 60; int hours = minutes / 60; @@ -233,6 +234,10 @@ QString Timecode::getStringTimecode(int frames, const double &fps) text.append(QString::number(minutes).rightJustified(2, '0', false)); text.append(':'); text.append(QString::number(seconds).rightJustified(2, '0', false)); + if (showFrames) { + text.append('.'); + text.append(QString::number(frms).rightJustified(2, '0', false)); + } return text; } diff --git a/src/timecode.h b/src/timecode.h index c98ac209..35a6f41e 100644 --- a/src/timecode.h +++ b/src/timecode.h @@ -50,7 +50,7 @@ public: int getDisplayFrameCount(const QString &duration, bool frameDisplay) const; int getFrameCount(const QString &duration) const; static QString getEasyTimecode(const GenTime & time, const double &fps); - static QString getStringTimecode(int frames, const double &fps); + static QString getStringTimecode(int frames, const double &fps, bool showFrames = false); const QString getDisplayTimecodeFromFrames(int frames, bool frameDisplay) const; const QString getTimecodeFromFrames(int frames) const; double fps() const; diff --git a/src/widgets/cutjobdialog_ui.ui b/src/widgets/cutjobdialog_ui.ui new file mode 100644 index 00000000..cd8a118f --- /dev/null +++ b/src/widgets/cutjobdialog_ui.ui @@ -0,0 +1,143 @@ + + + CutJobDialog_UI + + + + 0 + 0 + 359 + 171 + + + + Cut Clip + + + + + + 0 + + + + Cut file + + + + + + Add new clip to project + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + KFile::File|KFile::LocalOnly + + + + + + + Save to + + + + + + + + + + + + + + + Advanced + + + + + + + + + Extra parameters + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + KUrlRequester + QFrame +
kurlrequester.h
+
+
+ + + + buttonBox + accepted() + CutJobDialog_UI + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CutJobDialog_UI + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
-- 2.39.2