X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fprojectlist.cpp;h=f42b1863b62ac471dc1745f6f82bfb838156af80;hb=7ac877ea57663a02e9042939aa51eb700fbe4301;hp=42a93190d0add61a22b9cb39d694811a4b0bd9eb;hpb=8e035c977776259f445bed1cbc688a17caba1d67;p=kdenlive diff --git a/src/projectlist.cpp b/src/projectlist.cpp index 42a93190..f42b1863 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -219,22 +219,24 @@ QStringList InvalidDialog::getIds() const ProjectList::ProjectList(QWidget *parent) : - QWidget(parent), - m_render(NULL), - m_fps(-1), - m_commandStack(NULL), - m_openAction(NULL), - m_reloadAction(NULL), - m_extractAudioAction(NULL), - m_transcodeAction(NULL), - m_stabilizeAction(NULL), - m_doc(NULL), - m_refreshed(false), - m_allClipsProcessed(false), - m_thumbnailQueue(), - m_abortAllJobs(false), - m_closing(false), - m_invalidClipDialog(NULL) + QWidget(parent) + , m_render(NULL) + , m_fps(-1) + , m_menu(NULL) + , m_commandStack(NULL) + , m_openAction(NULL) + , m_reloadAction(NULL) + , m_extractAudioAction(NULL) + , m_transcodeAction(NULL) + , m_clipsActionsMenu(NULL) + , m_doc(NULL) + , m_refreshed(false) + , m_allClipsProcessed(false) + , m_thumbnailQueue() + , m_proxyAction(NULL) + , m_abortAllJobs(false) + , m_closing(false) + , m_invalidClipDialog(NULL) { qRegisterMetaType ("stringMap"); QVBoxLayout *layout = new QVBoxLayout; @@ -312,21 +314,21 @@ ProjectList::ProjectList(QWidget *parent) : connect(m_listView, SIGNAL(itemSelectionChanged()), this, SLOT(slotClipSelected())); connect(m_listView, SIGNAL(focusMonitor(bool)), this, SIGNAL(raiseClipMonitor(bool))); connect(m_listView, SIGNAL(pauseMonitor()), this, SIGNAL(pauseMonitor())); - connect(m_listView, SIGNAL(requestMenu(const QPoint &, QTreeWidgetItem *)), this, SLOT(slotContextMenu(const QPoint &, QTreeWidgetItem *))); + connect(m_listView, SIGNAL(requestMenu(QPoint,QTreeWidgetItem*)), this, SLOT(slotContextMenu(QPoint,QTreeWidgetItem*))); connect(m_listView, SIGNAL(addClip()), this, SIGNAL(pauseMonitor())); 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 *))); + connect(m_listView, SIGNAL(addClip(QList,QString,QString)), this, SLOT(slotAddClip(QList,QString,QString))); + connect(this, SIGNAL(addClip(QString,QString,QString)), this, SLOT(slotAddClip(QString,QString,QString))); + connect(m_listView, SIGNAL(addClipCut(QString,int,int)), this, SLOT(slotAddClipCut(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*))); - connect(this, SIGNAL(cancelRunningJob(const QString, stringMap )), this, SLOT(slotCancelRunningJob(const QString, stringMap))); - connect(this, SIGNAL(processLog(const QString, int , int, const QString)), this, SLOT(slotProcessLog(const QString, int , int, const QString))); + connect(this, SIGNAL(cancelRunningJob(QString,stringMap)), this, SLOT(slotCancelRunningJob(QString,stringMap))); + connect(this, SIGNAL(processLog(QString,int,int,QString)), this, SLOT(slotProcessLog(QString,int,int,QString))); - 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(updateJobStatus(QString,int,int,QString,QString,QString)), this, SLOT(slotUpdateJobStatus(QString,int,int,QString,QString,QString))); - connect(this, SIGNAL(gotProxy(const QString)), this, SLOT(slotGotProxyForId(const QString))); + connect(this, SIGNAL(gotProxy(QString)), this, SLOT(slotGotProxyForId(QString))); m_listViewDelegate = new ItemDelegate(m_listView); m_listView->setItemDelegate(m_listViewDelegate); @@ -353,7 +355,7 @@ ProjectList::~ProjectList() m_jobThreads.clearFutures(); if (!m_jobList.isEmpty()) qDeleteAll(m_jobList); m_jobList.clear(); - delete m_menu; + if (m_menu) delete m_menu; m_listView->blockSignals(true); m_listView->clear(); delete m_listViewDelegate; @@ -404,6 +406,10 @@ void ProjectList::setupMenu(QMenu *addMenu, QAction *defaultAction) void ProjectList::setupGeneratorMenu(const QHash& menus) { + if (!m_menu) { + kDebug()<<"Warning, menu was not created, something is wrong"; + return; + } if (!menus.contains("addMenu") && ! menus.value("addMenu") ) return; QMenu *menu = m_addButton->menu(); @@ -431,11 +437,11 @@ void ProjectList::setupGeneratorMenu(const QHash& menus) m_menu->addMenu(stabilizeMenu); if (stabilizeMenu->isEmpty()) stabilizeMenu->setEnabled(false); - m_stabilizeAction=stabilizeMenu; + m_clipsActionsMenu = stabilizeMenu; } - m_menu->addAction(m_reloadAction); - m_menu->addAction(m_proxyAction); + if (m_reloadAction) m_menu->addAction(m_reloadAction); + if (m_proxyAction) m_menu->addAction(m_proxyAction); if (menus.contains("inTimelineMenu") && menus.value("inTimelineMenu")){ QMenu* inTimelineMenu=menus.value("inTimelineMenu"); m_menu->addMenu(inTimelineMenu); @@ -715,10 +721,11 @@ void ProjectList::slotReloadClip(const QString &id) QDomElement e = item->toXml(); // Make sure we get the correct producer length if it was adjusted in timeline if (t == COLOR || t == IMAGE || t == SLIDESHOW || t == TEXT) { - int length = QString(clip->producerProperty("length")).toInt(); - if (length > 0 && !e.hasAttribute("length")) { - e.setAttribute("length", length); - } + int length = QString(clip->producerProperty("length")).toInt(); + if (length > 0 && !e.hasAttribute("length")) { + e.setAttribute("length", length); + } + e.setAttribute("duration", clip->getProperty("duration")); } resetThumbsProducer(clip); m_render->getFileProperties(e, item->clipId(), m_listView->iconSize().height(), true); @@ -834,18 +841,22 @@ void ProjectList::slotClipSelected() m_reloadAction->setEnabled(false); m_extractAudioAction->setEnabled(false); m_transcodeAction->setEnabled(false); - m_stabilizeAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); } else { if (item->type() == PROJECTSUBCLIPTYPE) { // this is a sub item, use base clip m_deleteButton->defaultAction()->setEnabled(true); clip = static_cast (item->parent()); - if (clip == NULL) kDebug() << "-----------ERROR"; + if (clip == NULL) { + kDebug() << "-----------ERROR"; + return; + } SubProjectItem *sub = static_cast (item); + if (clip->referencedClip()->getProducer() == NULL) m_render->getFileProperties(clip->referencedClip()->toXML(), clip->clipId(), m_listView->iconSize().height(), true); emit clipSelected(clip->referencedClip(), sub->zone()); m_extractAudioAction->setEnabled(false); m_transcodeAction->setEnabled(false); - m_stabilizeAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); m_reloadAction->setEnabled(false); adjustProxyActions(clip); return; @@ -853,12 +864,13 @@ void ProjectList::slotClipSelected() clip = static_cast (item); if (clip && clip->referencedClip()) emit clipSelected(clip->referencedClip()); + if (clip->referencedClip()->getProducer() == NULL) m_render->getFileProperties(clip->referencedClip()->toXML(), clip->clipId(), m_listView->iconSize().height(), true); m_editButton->defaultAction()->setEnabled(true); m_deleteButton->defaultAction()->setEnabled(true); m_reloadAction->setEnabled(true); m_extractAudioAction->setEnabled(true); m_transcodeAction->setEnabled(true); - m_stabilizeAction->setEnabled(true); + m_clipsActionsMenu->setEnabled(true); if (clip && clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) { m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp())); m_openAction->setEnabled(true); @@ -882,13 +894,14 @@ void ProjectList::slotClipSelected() m_reloadAction->setEnabled(false); m_extractAudioAction->setEnabled(true); m_transcodeAction->setEnabled(false); - m_stabilizeAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); } adjustProxyActions(clip); } void ProjectList::adjustProxyActions(ProjectItem *clip) const { + if (!m_proxyAction) return; if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == SLIDESHOW || clip->clipType() == AUDIO) { m_proxyAction->setEnabled(false); return; @@ -905,10 +918,10 @@ void ProjectList::adjustStabilizeActions(ProjectItem *clip) const { if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == SLIDESHOW) { - m_stabilizeAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); return; } - m_stabilizeAction->setEnabled(true); + m_clipsActionsMenu->setEnabled(true); } @@ -948,17 +961,16 @@ void ProjectList::slotUpdateClipProperties(const QString &id, QMap type() == PROJECTFOLDERTYPE) { if (column == 0) { FolderProjectItem *folder = static_cast (item); + if (item->text(0) == folder->groupName()) return; editFolder(item->text(0), folder->groupName(), folder->clipId()); folder->setGroupName(item->text(0)); m_doc->clipManager()->addFolder(folder->clipId(), item->text(0)); @@ -1068,20 +1081,24 @@ void ProjectList::slotCheckScrolling() void ProjectList::slotContextMenu(const QPoint &pos, QTreeWidgetItem *item) { + if (!m_menu) { + kDebug()<<"Warning, menu was not created, something is wrong"; + return; + } bool enable = item ? true : false; m_editButton->defaultAction()->setEnabled(enable); m_deleteButton->defaultAction()->setEnabled(enable); m_reloadAction->setEnabled(enable); m_extractAudioAction->setEnabled(enable); m_transcodeAction->setEnabled(enable); - m_stabilizeAction->setEnabled(enable); + m_clipsActionsMenu->setEnabled(enable); if (enable) { ProjectItem *clip = NULL; if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE) { clip = static_cast (item->parent()); m_extractAudioAction->setEnabled(false); m_transcodeAction->setEnabled(false); - m_stabilizeAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); adjustProxyActions(clip); } else if (m_listView->currentItem()->type() == PROJECTCLIPTYPE) { clip = static_cast (item); @@ -1094,7 +1111,7 @@ void ProjectList::slotContextMenu(const QPoint &pos, QTreeWidgetItem *item) } else { m_extractAudioAction->setEnabled(false); m_transcodeAction->setEnabled(false); - m_stabilizeAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); } if (clip && clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) { m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp())); @@ -1172,7 +1189,7 @@ void ProjectList::updateButtons() const m_openAction->setEnabled(true); m_reloadAction->setEnabled(true); m_transcodeAction->setEnabled(true); - m_stabilizeAction->setEnabled(true); + m_clipsActionsMenu->setEnabled(true); return; } else if (item && item->type() == PROJECTFOLDERTYPE && item->childCount() > 0) { @@ -1181,10 +1198,10 @@ void ProjectList::updateButtons() const else m_editButton->defaultAction()->setEnabled(false); } m_openAction->setEnabled(false); - m_reloadAction->setEnabled(false); + if (m_reloadAction) m_reloadAction->setEnabled(false); m_transcodeAction->setEnabled(false); - m_stabilizeAction->setEnabled(false); - m_proxyAction->setEnabled(false); + m_clipsActionsMenu->setEnabled(false); + if (m_proxyAction) m_proxyAction->setEnabled(false); } void ProjectList::selectItemById(const QString &clipId) @@ -1315,8 +1332,8 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) item = new ProjectItem(m_listView, clip, pixelSize); } if (item->data(0, DurationRole).isNull()) item->setData(0, DurationRole, i18n("Loading")); - connect(clip, SIGNAL(createProxy(const QString &)), this, SLOT(slotCreateProxy(const QString &))); - connect(clip, SIGNAL(abortProxy(const QString &, const QString &)), this, SLOT(slotAbortProxy(const QString, const QString))); + connect(clip, SIGNAL(createProxy(QString)), this, SLOT(slotCreateProxy(QString))); + connect(clip, SIGNAL(abortProxy(QString,QString)), this, SLOT(slotAbortProxy(QString,QString))); if (getProperties) { //item->setFlags(Qt::ItemIsSelectable); @@ -1458,7 +1475,9 @@ void ProjectList::getCachedThumbnail(ProjectItem *item) { if (!item) return; DocClipBase *clip = item->referencedClip(); - if (!clip) return; + if (!clip) { + return; + } QString cachedPixmap = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "thumbs/" + clip->getClipHash() + ".png"; if (QFile::exists(cachedPixmap)) { QPixmap pix(cachedPixmap); @@ -1477,7 +1496,7 @@ void ProjectList::getCachedThumbnail(ProjectItem *item) } } -QPixmap ProjectList::roundedPixmap(QImage img) +QPixmap ProjectList::roundedPixmap(const QImage &img) { QPixmap pix(img.width(), img.height()); pix.fill(Qt::transparent); @@ -1491,7 +1510,7 @@ QPixmap ProjectList::roundedPixmap(QImage img) return pix; } -QPixmap ProjectList::roundedPixmap(QPixmap source) +QPixmap ProjectList::roundedPixmap(const QPixmap &source) { QPixmap pix(source.width(), source.height()); pix.fill(Qt::transparent); @@ -1578,6 +1597,7 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged, QStr } if (clip->isPlaceHolder() == false && !hasPendingJob(item, PROXYJOB)) { QDomElement xml = clip->toXML(); + getCachedThumbnail(item); if (fpsChanged) { xml.removeAttribute("out"); xml.removeAttribute("file_hash"); @@ -1587,8 +1607,12 @@ void ProjectList::updateAllClips(bool displayRatioChanged, bool fpsChanged, QStr xml.removeAttribute("_replaceproxy"); if (replace) { resetThumbsProducer(clip); + m_render->getFileProperties(xml, clip->getId(), m_listView->iconSize().height(), replace); } - m_render->getFileProperties(xml, clip->getId(), m_listView->iconSize().height(), replace); + else if (item->numReferences() > 0) { + // In some cases, like slowmotion clips, the producer is not loaded automatically be MLT + m_render->getFileProperties(xml, clip->getId(), m_listView->iconSize().height(), replace); + } } else if (clip->isPlaceHolder()) { item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled); @@ -2024,12 +2048,12 @@ void ProjectList::setDocument(KdenliveDoc *doc) slotAddClip(list.at(i), false); m_listView->blockSignals(false); - connect(m_doc->clipManager(), SIGNAL(reloadClip(const QString &)), this, SLOT(slotReloadClip(const QString &))); - connect(m_doc->clipManager(), SIGNAL(modifiedClip(const QString &)), this, SLOT(slotModifiedClip(const QString &))); - connect(m_doc->clipManager(), SIGNAL(missingClip(const QString &)), this, SLOT(slotMissingClip(const QString &))); - connect(m_doc->clipManager(), SIGNAL(availableClip(const QString &)), this, SLOT(slotAvailableClip(const QString &))); - connect(m_doc->clipManager(), SIGNAL(checkAllClips(bool, bool, QStringList)), this, SLOT(updateAllClips(bool, bool, QStringList))); - connect(m_doc->clipManager(), SIGNAL(thumbReady(const QString &, int, QImage)), this, SLOT(slotSetThumbnail(const QString &, int, QImage))); + connect(m_doc->clipManager(), SIGNAL(reloadClip(QString)), this, SLOT(slotReloadClip(QString))); + connect(m_doc->clipManager(), SIGNAL(modifiedClip(QString)), this, SLOT(slotModifiedClip(QString))); + connect(m_doc->clipManager(), SIGNAL(missingClip(QString)), this, SLOT(slotMissingClip(QString))); + connect(m_doc->clipManager(), SIGNAL(availableClip(QString)), this, SLOT(slotAvailableClip(QString))); + connect(m_doc->clipManager(), SIGNAL(checkAllClips(bool,bool,QStringList)), this, SLOT(updateAllClips(bool,bool,QStringList))); + connect(m_doc->clipManager(), SIGNAL(thumbReady(QString,int,QImage)), this, SLOT(slotSetThumbnail(QString,int,QImage))); } void ProjectList::slotSetThumbnail(const QString &id, int framePos, QImage img) @@ -2186,12 +2210,87 @@ void ProjectList::slotRefreshClipThumbnail(QTreeWidgetItem *it, bool update) } } +void ProjectList::extractMetadata(DocClipBase *clip) +{ + CLIPTYPE t = clip->clipType(); + if (t != AV && t != VIDEO) { + // Currently, we only use exiftool on video files + return; + } + QMap props = clip->properties(); + if (KdenliveSettings::use_exiftool() && !props.contains("exiftool")) { + QMap meta; + QString url = clip->fileURL().path(); + //Check for Canon THM file + url = url.section('.', 0, -2) + ".THM"; + if (QFile::exists(url)) { + // Read the exif metadata embeded in the THM file + QProcess p; + QStringList args; + args << "-g" << "-args" << url; + p.start("exiftool", args); + p.waitForFinished(); + QString res = p.readAllStandardOutput(); + QStringList list = res.split("\n"); + foreach(QString tagline, list) { + if (tagline.startsWith("-File") || tagline.startsWith("-ExifTool")) continue; + QString tag = tagline.section(':', 1).simplified(); + if (tag.startsWith("ImageWidth") || tag.startsWith("ImageHeight")) continue; + if (!tag.section('=', 0, 0).isEmpty() && !tag.section('=', 1).simplified().isEmpty()) + meta.insert(tag.section('=', 0, 0), tag.section('=', 1).simplified()); + } + } else { + QString codecid = props.value("videocodecid").simplified(); + if (codecid == "h264") { + QProcess p; + QStringList args; + args << "-g" << "-args" << clip->fileURL().encodedPathAndQuery(); + p.start("exiftool", args); + p.waitForFinished(); + QString res = p.readAllStandardOutput(); + QStringList list = res.split("\n"); + foreach(QString tagline, list) { + if (!tagline.startsWith("-H264")) continue; + QString tag = tagline.section(':', 1); + if (tag.startsWith("ImageWidth") || tag.startsWith("ImageHeight")) continue; + meta.insert(tag.section('=', 0, 0), tag.section('=', 1)); + } + } + } + clip->setProperty("exiftool", "1"); + if (!meta.isEmpty()) { + clip->setMetadata(meta, "ExifTool"); + //checkCamcorderFilters(clip, meta); + } + } + if (KdenliveSettings::use_magicLantern() && !props.contains("magiclantern")) { + QMap meta; + QString url = clip->fileURL().path(); + url = url.section('.', 0, -2) + ".LOG"; + if (QFile::exists(url)) { + QFile file(url); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + while (!file.atEnd()) { + QString line = file.readLine().simplified(); + if (line.startsWith('#') || line.isEmpty() || !line.contains(':')) continue; + if (line.startsWith("CSV data")) break; + meta.insert(line.section(':', 0, 0).simplified(), line.section(':', 1).simplified()); + } + } + } + + if (!meta.isEmpty()) + clip->setMetadata(meta, "Magic Lantern"); + clip->setProperty("magiclantern", "1"); + } +} + void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const stringMap &properties, const stringMap &metadata, bool replace) { + QMutexLocker lock(&m_processMutex); QString toReload; ProjectItem *item = getItemById(clipId); - int queue = m_render->processingItems(); if (item && producer) { monitorItemEditing(false); DocClipBase *clip = item->referencedClip(); @@ -2204,6 +2303,8 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce } item->setProperties(properties, metadata); clip->setProducer(producer, replace); + extractMetadata(clip); + m_render->processingDone(clipId); // Proxy stuff QString size = properties.value("frame_size"); @@ -2243,7 +2344,11 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce } if (!toReload.isEmpty()) item->slotSetToolTip(); - } else kDebug() << "//////// COULD NOT FIND CLIP TO UPDATE PRPS..."; + } else { + kDebug() << "//////// COULD NOT FIND CLIP TO UPDATE PRPS..."; + m_render->processingDone(clipId); + } + int queue = m_render->processingItems(); if (queue == 0) { monitorItemEditing(true); if (item && m_thumbnailQueue.isEmpty()) { @@ -2256,6 +2361,9 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce } else if (KdenliveSettings::checkfirstprojectclip() && m_listView->topLevelItemCount() == 1 && m_refreshed && m_allClipsProcessed) { // this is the first clip loaded in project, check if we want to adjust project settings to the clip updatedProfile = adjustProjectProfileToItem(item); + if (updatedProfile == false) { + emit clipSelected(item->referencedClip()); + } } if (updatedProfile == false) { //emit clipSelected(item->referencedClip()); @@ -2272,8 +2380,9 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce return; } if (replace) toReload = clipId; - if (!toReload.isEmpty()) + if (!toReload.isEmpty()) { emit clipNeedsReload(toReload); + } } bool ProjectList::adjustProjectProfileToItem(ProjectItem *item) @@ -2475,7 +2584,7 @@ void ProjectList::slotSelectClip(const QString &ix) m_reloadAction->setEnabled(true); m_extractAudioAction->setEnabled(true); m_transcodeAction->setEnabled(true); - m_stabilizeAction->setEnabled(true); + m_clipsActionsMenu->setEnabled(true); if (clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) { m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp())); m_openAction->setEnabled(true); @@ -2531,9 +2640,9 @@ KUrl::List ProjectList::getConditionalUrls(const QString &condition) const return result; } -QStringList ProjectList::getConditionalIds(const QString &condition) const +QMap ProjectList::getConditionalIds(const QString &condition) const { - QStringList result; + QMap result; ProjectItem *item; QList list = m_listView->selectedItems(); for (int i = 0; i < list.count(); i++) { @@ -2554,7 +2663,7 @@ QStringList ProjectList::getConditionalIds(const QString &condition) const else if (condition.startsWith("acodec") && !clip->hasAudioCodec(condition.section('=', 1, 1))) continue; } - result.append(item->clipId()); + result.insert(item->clipId(), item->clipUrl().path()); } return result; } @@ -2619,7 +2728,7 @@ void ProjectList::addClipCut(const QString &id, int in, int out, const QString d m_listView->scrollToItem(sub); m_listView->editItem(sub, 1); } - m_doc->clipManager()->requestThumbs(QString('#' + id), QList () << in); + m_doc->clipManager()->slotRequestThumbs(QString('#' + id), QList () << in); monitorItemEditing(true); } emit projectModified(); @@ -2641,7 +2750,7 @@ void ProjectList::removeClipCut(const QString &id, int in, int out) emit projectModified(); } -SubProjectItem *ProjectList::getSubItem(ProjectItem *clip, QPoint zone) +SubProjectItem *ProjectList::getSubItem(ProjectItem *clip, const QPoint &zone) { SubProjectItem *sub = NULL; if (clip) { @@ -2669,7 +2778,7 @@ void ProjectList::slotUpdateClipCut(QPoint p) m_commandStack->push(command); } -void ProjectList::doUpdateClipCut(const QString &id, const QPoint oldzone, const QPoint zone, const QString &comment) +void ProjectList::doUpdateClipCut(const QString &id, const QPoint &oldzone, const QPoint &zone, const QString &comment) { ProjectItem *clip = getItemById(id); SubProjectItem *sub = getSubItem(clip, oldzone); @@ -2689,7 +2798,7 @@ void ProjectList::slotForceProcessing(const QString &id) m_render->forceProcessing(id); } -void ProjectList::slotAddOrUpdateSequence(const QString frameName) +void ProjectList::slotAddOrUpdateSequence(const QString &frameName) { QString fileName = KUrl(frameName).fileName().section('_', 0, -2); QStringList list; @@ -2751,7 +2860,7 @@ QMap ProjectList::getProxies() return list; } -void ProjectList::slotCreateProxy(const QString id) +void ProjectList::slotCreateProxy(const QString &id) { ProjectItem *item = getItemById(id); if (!item || hasPendingJob(item, PROXYJOB) || item->referencedClip()->isPlaceHolder()) return; @@ -2860,14 +2969,14 @@ void ProjectList::slotCutClipJob(const QString &id, QPoint zone) void ProjectList::slotTranscodeClipJob(const QString &condition, QString params, QString desc) { QStringList existingFiles; - QStringList ids = getConditionalIds(condition); + QMap ids = getConditionalIds(condition); + QMap::const_iterator i = ids.constBegin(); QStringList destinations; - foreach(const QString &id, ids) { - ProjectItem *item = getItemById(id); - if (!item) continue; - QString newFile = params.section(' ', -1).replace("%1", item->clipUrl().path()); - destinations << newFile; + while (i != ids.constEnd()) { + QString newFile = params.section(' ', -1).replace("%1", i.value()); + destinations << newFile; if (QFile::exists(newFile)) existingFiles << newFile; + ++i; } if (!existingFiles.isEmpty()) { if (KMessageBox::warningContinueCancelList(this, i18n("The transcoding job will overwrite the following files:"), existingFiles) == KMessageBox::Cancel) return; @@ -2900,10 +3009,11 @@ void ProjectList::slotTranscodeClipJob(const QString &condition, QString params, params = ui.extra_params->toPlainText().simplified(); KdenliveSettings::setAdd_new_clip(ui.add_clip->isChecked()); int index = 0; - foreach(const QString &id, ids) { - ProjectItem *item = getItemById(id); + i = ids.constBegin(); + while (i != ids.constEnd()) { + ProjectItem *item = getItemById(i.key()); if (!item || !item->referencedClip()) continue; - QString src = item->clipUrl().path(); + QString src = i.value(); QString dest; if (ids.count() > 1) { dest = destinations.at(index); @@ -2919,13 +3029,14 @@ void ProjectList::slotTranscodeClipJob(const QString &condition, QString params, jobParams << duration; jobParams << QString::number(KdenliveSettings::add_new_clip()); jobParams << params; - CutClipJob *job = new CutClipJob(item->clipType(), id, jobParams); + CutClipJob *job = new CutClipJob(item->clipType(), i.key(), jobParams); if (job->isExclusive() && hasPendingJob(item, job->jobType)) { delete job; continue; } m_jobList.append(job); setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage()); + ++i; } delete d; slotCheckJobProcess(); @@ -2944,26 +3055,25 @@ void ProjectList::slotCheckJobProcess() } } if (m_jobList.isEmpty()) return; - int count = 0; + m_jobMutex.lock(); + int count = 0; for (int i = 0; i < m_jobList.count(); i++) { - if (m_jobList.at(i)->jobStatus == JOBWORKING || m_jobList.at(i)->jobStatus == JOBWAITING) + if (m_jobList.at(i)->status() == JOBWORKING || m_jobList.at(i)->status() == JOBWAITING) count ++; else { // remove finished jobs AbstractClipJob *job = m_jobList.takeAt(i); - delete job; + job->deleteLater(); i--; } } - - emit jobCount(count); + emit jobCount(count); m_jobMutex.unlock(); - if (m_jobThreads.futures().isEmpty() || m_jobThreads.futures().count() < KdenliveSettings::proxythreads()) m_jobThreads.addFuture(QtConcurrent::run(this, &ProjectList::slotProcessJobs)); } -void ProjectList::slotAbortProxy(const QString id, const QString path) +void ProjectList::slotAbortProxy(const QString &id, const QString &path) { Q_UNUSED(path) @@ -2982,14 +3092,14 @@ void ProjectList::slotProcessJobs() int count = 0; m_jobMutex.lock(); for (int i = 0; i < m_jobList.count(); i++) { - if (m_jobList.at(i)->jobStatus == JOBWAITING) { + if (m_jobList.at(i)->status() == JOBWAITING) { if (job == NULL) { - m_jobList.at(i)->jobStatus = JOBWORKING; + m_jobList.at(i)->setStatus(JOBWORKING); job = m_jobList.at(i); } count++; } - else if (m_jobList.at(i)->jobStatus == JOBWORKING) + else if (m_jobList.at(i)->status() == JOBWORKING) count ++; } // Set jobs count @@ -3021,27 +3131,27 @@ void ProjectList::slotProcessJobs() file.close(); QFile::remove(destination); } - 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))); + connect(job, SIGNAL(jobProgress(QString,int,int)), this, SIGNAL(processLog(QString,int,int))); + connect(job, SIGNAL(cancelRunningJob(QString,stringMap)), this, SIGNAL(cancelRunningJob(QString,stringMap))); if (job->jobType == MLTJOB) { MeltJob *jb = static_cast (job); jb->setProducer(currentClip->getProducer(), currentClip->fileURL()); if (jb->isProjectFilter()) - connect(job, SIGNAL(gotFilterJobResults(QString,int, int, stringMap,stringMap)), this, SLOT(slotGotFilterJobResults(QString,int, int,stringMap,stringMap))); + connect(job, SIGNAL(gotFilterJobResults(QString,int,int,stringMap,stringMap)), this, SLOT(slotGotFilterJobResults(QString,int,int,stringMap,stringMap))); else - connect(job, SIGNAL(gotFilterJobResults(QString,int, int, stringMap,stringMap)), this, SIGNAL(gotFilterJobResults(QString,int, int,stringMap,stringMap))); + connect(job, SIGNAL(gotFilterJobResults(QString,int,int,stringMap,stringMap)), this, SIGNAL(gotFilterJobResults(QString,int,int,stringMap,stringMap))); } job->startJob(); - if (job->jobStatus == JOBDONE) { + if (job->status() == JOBDONE) { emit updateJobStatus(job->clipId(), job->jobType, JOBDONE); //TODO: replace with more generic clip replacement framework if (job->jobType == PROXYJOB) emit gotProxy(job->clipId()); if (job->addClipToProject()) { emit addClip(destination, QString(), QString()); } - } else if (job->jobStatus == JOBCRASHED || job->jobStatus == JOBABORTED) { - emit updateJobStatus(job->clipId(), job->jobType, job->jobStatus, job->errorMessage(), QString(), job->logDetails()); + } else if (job->status() == JOBCRASHED || job->status() == JOBABORTED) { + emit updateJobStatus(job->clipId(), job->jobType, job->status(), job->errorMessage(), QString(), job->logDetails()); } } // Thread finished, cleanup & update count @@ -3118,7 +3228,7 @@ void ProjectList::updateProxyConfig() else delete command; } -void ProjectList::slotProcessLog(const QString id, int progress, int type, const QString message) +void ProjectList::slotProcessLog(const QString &id, int progress, int type, const QString &message) { ProjectItem *item = getItemById(id); setJobStatus(item, (JOBTYPE) type, JOBWORKING, progress, message); @@ -3257,8 +3367,8 @@ void ProjectList::setJobStatus(ProjectItem *item, JOBTYPE jobType, CLIPJOBSTATUS void ProjectList::monitorItemEditing(bool enable) { - if (enable) connect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int))); - else disconnect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int))); + if (enable) connect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemEdited(QTreeWidgetItem*,int))); + else disconnect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemEdited(QTreeWidgetItem*,int))); } QStringList ProjectList::expandedFolders() const @@ -3345,7 +3455,7 @@ bool ProjectList::hasPendingJob(ProjectItem *item, JOBTYPE type) 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 == type && (job->jobStatus == JOBWAITING || job->jobStatus == JOBWORKING)) return true; + if (job->clipId() == item->clipId() && job->jobType == type && (job->status() == JOBWAITING || job->status() == JOBWORKING)) return true; } return false; @@ -3437,7 +3547,7 @@ 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)) { + if (m_jobList.at(i)->clipId() == id && (m_jobList.at(i)->status() == JOBWAITING || m_jobList.at(i)->status() == JOBWORKING)) { // discard this job result << m_jobList.at(i)->description; } @@ -3460,8 +3570,8 @@ void ProjectList::slotStartFilterJob(ItemInfo info, const QString&id, const QStr ProjectItem *item = getItemById(id); if (!item) return; QStringList jobParams; - jobParams << QString::number(info.cropStart.frames(m_fps)) << QString::number((info.cropStart + info.cropDuration).frames(m_fps)); - jobParams << QString() << filterName << filterParams << consumer << consumerParams << QString::number(info.startPos.frames(m_fps)) << QString::number(info.track); + jobParams << QString::number((int) info.cropStart.frames(m_fps)) << QString::number((int) (info.cropStart + info.cropDuration).frames(m_fps)); + jobParams << QString() << filterName << filterParams << consumer << consumerParams << QString::number((int) info.startPos.frames(m_fps)) << QString::number(info.track); MeltJob *job = new MeltJob(item->clipType(), id, jobParams, extraParams); if (job->isExclusive() && hasPendingJob(item, job->jobType)) { delete job; @@ -3475,19 +3585,63 @@ void ProjectList::slotStartFilterJob(ItemInfo info, const QString&id, const QStr void ProjectList::startClipFilterJob(const QString &filterName, const QString &condition) { - QStringList ids = getConditionalIds(condition); - QString destination; - ProjectItem *item = getItemById(ids.at(0)); + QMap ids = getConditionalIds(condition); + QStringList destination; + QMap::const_iterator first = ids.constBegin(); + if (first == ids.constEnd()) { + emit displayMessage(i18n("Cannot find clip to process filter %1", filterName), -2, ErrorMessage); + return; + } + ProjectItem *item = getItemById(first.key()); if (!item) { emit displayMessage(i18n("Cannot find clip to process filter %1", filterName), -2, ErrorMessage); return; } if (ids.count() == 1) { - destination = item->clipUrl().path(); + destination << item->clipUrl().path(); } else { - destination = item->clipUrl().directory(); + destination = ids.values(); + } + if (filterName == "framebuffer") { + Mlt::Profile profile; + QStringList keys = ids.keys(); + int ix = 0; + foreach(const QString &url, destination) { + QString prodstring = QString("framebuffer:" + url + "?-1"); + Mlt::Producer *reversed = new Mlt::Producer(profile, prodstring.toUtf8().constData()); + if (!reversed || !reversed->is_valid()) { + emit displayMessage(i18n("Cannot reverse clip"), -2, ErrorMessage); + continue; + } + QString dest = url + ".mlt"; + if (QFile::exists(dest)) { + if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", dest)) == KMessageBox::No) continue; + } + Mlt::Consumer *cons = new Mlt::Consumer(profile, "xml", dest.toUtf8().constData()); + if (cons == NULL || !cons->is_valid()) { + emit displayMessage(i18n("Cannot render reversed clip"), -2, ErrorMessage); + continue; + } + Mlt::Playlist list; + list.insert_at(0, reversed, 0); + delete reversed; + cons->connect(list); + cons->run(); + delete cons; + QString groupId; + QString groupName; + DocClipBase *base = m_doc->clipManager()->getClipById(keys.at(ix)); + if (base) { + groupId = base->getProperty("groupid"); + groupName = base->getProperty("groupname"); + } + emit addClip(dest, groupId, groupName); + ix++; + } + return; } + if (filterName == "motion_est") { // Show config dialog QPointer d = new QDialog(this); @@ -3508,16 +3662,22 @@ void ProjectList::startClipFilterJob(const QString &filterName, const QString &c // Producer params jobParams << QString(); // Filter params, use a smaller region of the image to speed up operation - jobParams << filterName << "bounding=\"25%x25%:15%x15\" shot_change_list=0 denoise=0"; + // In fact, it's faster to rescale whole image than using part of it (bounding=\"25%x25%:15%x15\") + jobParams << filterName << "shot_change_list=0 denoise=0"; // Consumer - jobParams << "null" << "all=1 terminate_on_pause=1 real_time=-1"; + jobParams << "null" << "all=1 terminate_on_pause=1 real_time=-1 rescale=nearest deinterlace_method=onefield top_field_first=-1"; QMap extraParams; extraParams.insert("key", "shot_change_list"); extraParams.insert("projecttreefilter", "1"); QString keyword("%count"); extraParams.insert("resultmessage", i18n("Found %1 scenes.", keyword)); + extraParams.insert("resize_profile", "160"); + if (ui.store_data->isChecked()) { + // We want to save result as clip metadata + extraParams.insert("storedata", "1"); + } if (ui.zone_only->isChecked()) { - // We want to create markers + // We want to analyze only clip zone extraParams.insert("zoneonly", "1"); } if (ui.add_markers->isChecked()) { @@ -3529,14 +3689,14 @@ void ProjectList::startClipFilterJob(const QString &filterName, const QString &c extraParams.insert("cutscenes", "1"); } delete d; - processClipJob(ids, QString(), false, jobParams, i18n("Auto split"), extraParams); + processClipJob(ids.keys(), QString(), false, jobParams, i18n("Auto split"), extraParams); } else { - QPointer d = new ClipStabilize(destination, ids.count(), filterName); + QPointer d = new ClipStabilize(destination, filterName); if (d->exec() == QDialog::Accepted) { QMap extraParams; extraParams.insert("producer_profile", "1"); - processClipJob(ids, d->destination(), d->autoAddClip(), d->params(), d->desc(), extraParams); + processClipJob(ids.keys(), d->destination(), d->autoAddClip(), d->params(), d->desc(), extraParams); } delete d; } @@ -3590,9 +3750,10 @@ void ProjectList::processClipJob(QStringList ids, const QString&destination, boo job->description = description; m_jobList.append(job); setJobStatus(item, job->jobType, JOBWAITING, 0, job->statusMessage()); + slotCheckJobProcess(); } - slotCheckJobProcess(); } + void ProjectList::slotPrepareJobsMenu() { @@ -3646,9 +3807,10 @@ void ProjectList::slotClosePopup() void ProjectList::slotGotFilterJobResults(QString id, int , int , stringMap results, stringMap filterInfo) { // Currently, only the first value of results is used - kDebug()<<"// FILTER RES:\n"< meta) +{ + KConfig conf("camcorderfilters.rc", KConfig::CascadeConfig, "appdata"); + QStringList groups = conf.groupList(); + foreach(QString grp, groups) { + if (!meta.contains(grp)) continue; + KConfigGroup group(&conf, grp); + QString value = group.readEntry(meta.value(grp)); + if (value.isEmpty()) continue; + clip->setProperty(value.section(' ', 0, 0), value.section(' ', 1)); + break; + } +}*/ + #include "projectlist.moc"