From 525c7c608d9f74d569ecbe88f8565eee5d3bfec4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Sat, 8 Sep 2012 00:15:33 +0200 Subject: [PATCH] Improve support for multi-stream clips: http://kdenlive.org/mantis/view.php?id=2717 --- src/clipmanager.cpp | 31 ++++++++------ src/clipmanager.h | 4 +- src/kdenlivedoc.cpp | 9 ++-- src/kdenlivedoc.h | 4 +- src/lib/audio/audioInfo.cpp | 3 +- src/mainwindow.cpp | 11 ++--- src/mainwindow.h | 2 +- src/projectlist.cpp | 30 ++++++++----- src/renderer.cpp | 82 +++++++++++++++++++++++++++++++++++- src/renderer.h | 5 +++ src/utils/resourcewidget.cpp | 2 +- src/utils/resourcewidget.h | 2 +- 12 files changed, 144 insertions(+), 41 deletions(-) diff --git a/src/clipmanager.cpp b/src/clipmanager.cpp index 13f4e5cf..c6552696 100644 --- a/src/clipmanager.cpp +++ b/src/clipmanager.cpp @@ -474,11 +474,15 @@ void ClipManager::resetProducersList(const QList prods, bool d void ClipManager::slotAddClip(KIO::Job *job, const KUrl &, const KUrl &dst) { KIO::MetaData meta = job->metaData(); + QMap data; + data.insert("group", meta.value("group")); + data.insert("groupid", meta.value("groupid")); + data.insert("comment", meta.value("comment")); kDebug()<<"Finished copying: "< data) { QUndoCommand *addClips = new QUndoCommand(); // Update list of removable volumes @@ -486,7 +490,7 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c listRemovableVolumes(); foreach(const KUrl & file, urls) { if (QFile::exists(file.path())) {//KIO::NetAccess::exists(file, KIO::NetAccess::SourceSide, NULL)) { - if (!getClipByResource(file.path()).empty()) { + if (!data.contains("bypassDuplicate") && !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; } @@ -500,9 +504,9 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c //KIO::filesize_t m_requestedSize; KIO::CopyJob *copyjob = KIO::copy (file, KUrl(sourcesFolder)); //TODO: for some reason, passing metadata does not work... - copyjob->addMetaData("group", group); - copyjob->addMetaData("groupId", groupId); - copyjob->addMetaData("comment", comment); + copyjob->addMetaData("group", data.value("group")); + copyjob->addMetaData("groupId", data.value("groupId")); + copyjob->addMetaData("comment", data.value("comment")); copyjob->ui()->setWindow(kapp->activeWindow()); connect(copyjob, SIGNAL(copyingDone(KIO::Job *, const KUrl &, const KUrl &, time_t, bool, bool)), this, SLOT(slotAddClip(KIO::Job *, const KUrl &, const KUrl &))); continue; @@ -515,11 +519,14 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c prod.setAttribute("resource", file.path()); uint id = m_clipIdCounter++; prod.setAttribute("id", QString::number(id)); - if (!comment.isEmpty()) prod.setAttribute("description", comment); - if (!group.isEmpty()) { - prod.setAttribute("groupname", group); - prod.setAttribute("groupid", groupId); + if (data.contains("comment")) prod.setAttribute("description", data.value("comment")); + if (data.contains("group")) { + prod.setAttribute("groupname", data.value("group")); + prod.setAttribute("groupid", data.value("groupId")); } + if (data.contains("video_index")) prod.setAttribute("video_index", data.value("video_index")); + if (data.contains("audio_index")) prod.setAttribute("audio_index", data.value("audio_index")); + KMimeType::Ptr type = KMimeType::findByUrl(file); if (type->name().startsWith("image/")) { prod.setAttribute("type", (int) IMAGE); @@ -576,9 +583,9 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c } } -void ClipManager::slotAddClipFile(const KUrl &url, const QString &group, const QString &groupId, const QString &comment) +void ClipManager::slotAddClipFile(const KUrl &url, QMap data) { - slotAddClipList(KUrl::List(url), group, groupId, comment); + slotAddClipList(KUrl::List(url), data); } void ClipManager::slotAddXmlClipFile(const QString &name, const QDomElement &xml, const QString &group, const QString &groupId) diff --git a/src/clipmanager.h b/src/clipmanager.h index 624bb9b5..512edd20 100644 --- a/src/clipmanager.h +++ b/src/clipmanager.h @@ -88,14 +88,14 @@ Q_OBJECT public: * @param url file to add * @param group name of the group to insert the file in (can be empty) * @param groupId id of the group (if any) */ - void slotAddClipFile(const KUrl &url, const QString &group, const QString &groupId, const QString &comment = QString()); + void slotAddClipFile(const KUrl &url, QMap data); /** @brief Adds a list of files to the project. * @param urls files to add * @param group name of the group to insert the files in (can be empty) * @param groupId id of the group (if any) * It checks for duplicated items and asks to the user for instructions. */ - void slotAddClipList(const KUrl::List urls, const QString &group, const QString &groupId, const QString &comment = QString()); + void slotAddClipList(const KUrl::List urls, QMap data); void slotAddTextClipFile(const QString &titleName, int out, const QString &xml, const QString &group, const QString &groupId); void slotAddTextTemplateClip(QString titleName, const KUrl &path, const QString &group, const QString &groupId); void slotAddXmlClipFile(const QString &name, const QDomElement &xml, const QString &group, const QString &groupId); diff --git a/src/kdenlivedoc.cpp b/src/kdenlivedoc.cpp index e5597b54..be19ccfd 100644 --- a/src/kdenlivedoc.cpp +++ b/src/kdenlivedoc.cpp @@ -342,6 +342,7 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup //kDebug() << "// SETTING SCENE LIST:\n\n" << m_document.toString(); connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); + connect(m_render, SIGNAL(addClip(const KUrl &, stringMap)), this, SLOT(slotAddClipFile(const KUrl &, stringMap))); } KdenliveDoc::~KdenliveDoc() @@ -1216,17 +1217,17 @@ void KdenliveDoc::deleteClip(const QString &clipId) emit signalDeleteProjectClip(clipId); } -void KdenliveDoc::slotAddClipList(const KUrl::List urls, const QString &group, const QString &groupId) +void KdenliveDoc::slotAddClipList(const KUrl::List urls, stringMap data) { - m_clipManager->slotAddClipList(urls, group, groupId); + m_clipManager->slotAddClipList(urls, data); //emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); setModified(true); } -void KdenliveDoc::slotAddClipFile(const KUrl &url, const QString &group, const QString &groupId, const QString &comment) +void KdenliveDoc::slotAddClipFile(const KUrl &url, stringMap data) { - m_clipManager->slotAddClipFile(url, group, groupId, comment); + m_clipManager->slotAddClipFile(url, data); emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); setModified(true); } diff --git a/src/kdenlivedoc.h b/src/kdenlivedoc.h index cc74e5d3..5c658590 100644 --- a/src/kdenlivedoc.h +++ b/src/kdenlivedoc.h @@ -83,8 +83,7 @@ Q_OBJECT public: * * If the clip wasn't added before, it tries to add it to the project. */ bool addClipInfo(QDomElement elem, QDomElement orig, QString clipId); - void slotAddClipFile(const KUrl &url, const QString &group = QString(), const QString &groupId = QString(), const QString &comment = QString()); - void slotAddClipList(const KUrl::List urls, const QString &group = QString(), const QString &groupId = QString()); + void slotAddClipList(const KUrl::List urls, stringMap data = QMap ()); void deleteClip(const QString &clipId); int getFramePos(QString duration); DocClipBase *getBaseClip(const QString &clipId); @@ -222,6 +221,7 @@ public slots: * @param mod (optional) true if the document has to be saved */ void setModified(bool mod = true); void checkProjectClips(bool displayRatioChanged = false, bool fpsChanged = false); + void slotAddClipFile(const KUrl &url, stringMap data = QMap ()); private slots: void slotAutoSave(); diff --git a/src/lib/audio/audioInfo.cpp b/src/lib/audio/audioInfo.cpp index 56e23095..cb946cac 100644 --- a/src/lib/audio/audioInfo.cpp +++ b/src/lib/audio/audioInfo.cpp @@ -22,14 +22,13 @@ m_producer(NULL) // Mlt::Factory::init(NULL); // Get the number of streams and add the information of each of them if it is an audio stream. - int streams = atoi(producer->get("meta.media.nb_streams")); + int streams = producer->get_int("meta.media.nb_streams"); for (int i = 0; i < streams; i++) { QByteArray propertyName = QString("meta.media.%1.stream.type").arg(i).toLocal8Bit(); const char* streamtype = producer->get(propertyName.data()); if (streamtype && strcmp("audio", streamtype) == 0) { m_list << new AudioStreamInfo(producer, i); } - } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 83761b64..e1ab4dc9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3102,16 +3102,17 @@ void MainWindow::slotEditItemDuration() m_activeTimeline->projectView()->editItemDuration(); } -void MainWindow::slotAddProjectClip(KUrl url, const QString &comment) +void MainWindow::slotAddProjectClip(KUrl url, QMap data) { - if (m_activeDocument) - m_activeDocument->slotAddClipFile(url, QString(), QString(), comment); + if (m_activeDocument) { + m_activeDocument->slotAddClipFile(url, data); + } } void MainWindow::slotAddProjectClipList(KUrl::List urls) { if (m_activeDocument) - m_activeDocument->slotAddClipList(urls, QString()); + m_activeDocument->slotAddClipList(urls); } void MainWindow::slotAddTransition(QAction *result) @@ -4496,7 +4497,7 @@ void MainWindow::slotDownloadResources() if (m_activeDocument) currentFolder = m_activeDocument->projectFolder().path(); else currentFolder = KdenliveSettings::defaultprojectfolder(); ResourceWidget *d = new ResourceWidget(currentFolder); - connect(d, SIGNAL(addClip(KUrl, const QString &)), this, SLOT(slotAddProjectClip(KUrl, const QString &))); + connect(d, SIGNAL(addClip(KUrl, QMap )), this, SLOT(slotAddProjectClip(KUrl, QMap ))); d->show(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 79774c36..cfcdfbae 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -425,7 +425,7 @@ private slots: void slotSelectAddTimelineTransition(); void slotAddVideoEffect(QAction *result); void slotAddTransition(QAction *result); - void slotAddProjectClip(KUrl url, const QString &comment = QString()); + void slotAddProjectClip(KUrl url, QMap data = QMap ()); void slotAddProjectClipList(KUrl::List urls); void slotShowClipProperties(DocClipBase *clip); void slotShowClipProperties(QList cliplist, QMap commonproperties); diff --git a/src/projectlist.cpp b/src/projectlist.cpp index c5d1e01b..201e2cfd 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -946,7 +946,7 @@ void ProjectList::slotUpdateClipProperties(const QString &id, QMap setEnabled(false); const QString parent = clip->getProperty("groupid"); + QString groupName = clip->getProperty("groupname"); ProjectItem *item = NULL; - kDebug()<<"// Adding clip 1"; monitorItemEditing(false); if (!parent.isEmpty()) { FolderProjectItem *parentitem = getFolderItemById(parent); if (!parentitem) { QStringList text; - QString groupName = clip->getProperty("groupname"); //kDebug() << "Adding clip to new group: " << groupName; if (groupName.isEmpty()) groupName = i18n("Folder"); text << groupName; @@ -1311,7 +1310,6 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) if (item == NULL) { item = new ProjectItem(m_listView, clip); } - kDebug()<<"// Adding clip 2"; 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))); @@ -1323,6 +1321,10 @@ void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties) //item->setFlags(Qt::ItemIsSelectable); m_listView->processLayout(); QDomElement e = clip->toXML().cloneNode().toElement(); + if (!groupName.isEmpty()) { + e.setAttribute("groupId", parent); + e.setAttribute("group", groupName); + } e.removeAttribute("file_hash"); resetThumbsProducer(clip); m_render->getFileProperties(e, clip->getId(), m_listView->iconSize().height(), true); @@ -1735,9 +1737,15 @@ void ProjectList::slotAddClip(const QList givenList, const QString &group if (givenList.isEmpty() && !list.isEmpty()) { QStringList groupInfo = getGroup(); - m_doc->slotAddClipList(list, groupInfo.at(0), groupInfo.at(1)); + QMap data; + data.insert("group", groupInfo.at(0)); + data.insert("groupId", groupInfo.at(1)); + m_doc->slotAddClipList(list, data); } else if (!list.isEmpty()) { - m_doc->slotAddClipList(list, groupName, groupId); + QMap data; + data.insert("group", groupName); + data.insert("groupId", groupId); + m_doc->slotAddClipList(list, data); } if (!foldersList.isEmpty()) { @@ -1754,8 +1762,12 @@ void ProjectList::slotAddClip(const QList givenList, const QString &group folder = getFolderItemByName(folderName); } } - if (folder) - m_doc->slotAddClipList(urls, folder->groupName(), folder->clipId()); + if (folder) { + QMap data; + data.insert("group", folder->groupName()); + data.insert("groupId", folder->clipId()); + m_doc->slotAddClipList(urls, data); + } else m_doc->slotAddClipList(urls); } } @@ -2132,7 +2144,6 @@ void ProjectList::slotRefreshClipThumbnail(QTreeWidgetItem *it, bool update) void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const stringMap &properties, const stringMap &metadata, bool replace) { QString toReload; - kDebug()<<"// CLIP LOADED 1;"; ProjectItem *item = getItemById(clipId); int queue = m_render->processingItems(); if (item && producer) { @@ -2190,7 +2201,6 @@ void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Produce if (queue == 0) { monitorItemEditing(true); if (item && m_thumbnailQueue.isEmpty()) { - kDebug()<<"// CLIP LOADED;"; if (!item->hasProxy() || m_render->activeClipId() == item->clipId()) m_listView->setCurrentItem(item); bool updatedProfile = false; diff --git a/src/renderer.cpp b/src/renderer.cpp index 18ea5555..dde112eb 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -120,6 +120,7 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi m_blackClip(NULL), m_winid(winid) { + qRegisterMetaType ("stringMap"); analyseAudio = KdenliveSettings::monitor_audio(); if (profile.isEmpty()) profile = KdenliveSettings::current_profile(); buildConsumer(profile); @@ -129,6 +130,7 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi m_refreshTimer.setSingleShot(true); m_refreshTimer.setInterval(70); connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh())); + connect(this, SIGNAL(multiStreamFound(const QString &,QList,QList,stringMap)), this, SLOT(slotMultiStreamProducerFound(const QString &,QList,QList,stringMap))); } Render::~Render() @@ -864,6 +866,28 @@ void Render::processFileProperties() // Get frame rate int vindex = producer->get_int("video_index"); + + // List streams + int streams = producer->get_int("meta.media.nb_streams"); + QList audio_list; + QList video_list; + for (int i = 0; i < streams; i++) { + QByteArray propertyName = QString("meta.media.%1.stream.type").arg(i).toLocal8Bit(); + QString type = producer->get(propertyName.data()); + if (type == "audio") audio_list.append(i); + else if (type == "video") video_list.append(i); + } + + if (!info.xml.hasAttribute("video_index") && video_list.count() > 1) { + // Clip has more than one video stream, ask which one should be used + QMap data; + if (info.xml.hasAttribute("group")) data.insert("group", info.xml.attribute("group")); + if (info.xml.hasAttribute("groupId")) data.insert("groupId", info.xml.attribute("groupId")); + emit multiStreamFound(path, audio_list, video_list, data); + // Force video index so that when reloading the clip we don't ask again for other streams + filePropertyMap["video_index"] = QString::number(vindex); + } + if (vindex > -1) { snprintf(property, sizeof(property), "meta.media.%d.stream.frame_rate", vindex); if (producer->get(property)) @@ -4466,7 +4490,63 @@ bool Render::getBlackMagicOutputDeviceList(KComboBox *devicelist) return true; } - +void Render::slotMultiStreamProducerFound(const QString path, QList audio_list, QList video_list, stringMap data) +{ + int width = 60.0 * m_mltProfile->dar(); + int swidth = 60.0 * m_mltProfile->width() / m_mltProfile->height(); + if (width % 2 == 1) width++; + + KDialog dialog(qApp->activeWindow()); + dialog.setCaption("Multi Stream Clip"); + dialog.setButtons(KDialog::Ok | KDialog::Cancel); + dialog.setButtonText(KDialog::Ok, i18n("Import selected clips")); + QWidget *content = new QWidget(&dialog); + dialog.setMainWidget(content); + QVBoxLayout *vbox = new QVBoxLayout(content); + QLabel *lab1 = new QLabel(i18n("Additional streams for clip\n %1", path), content); + vbox->addWidget(lab1); + QList groupList; + QList comboList; + // We start loading the list at 1, video index 0 should already be loaded + for (int j = 1; j < video_list.count(); j++) { + Mlt::Producer multiprod(* m_mltProfile, path.toUtf8().constData()); + multiprod.set("video_index", video_list.at(j)); + kDebug()<<"// LOADING: "<setProperty("vindex", video_list.at(j)); + groupList << streamFrame; + streamFrame->setCheckable(true); + streamFrame->setChecked(false); + QVBoxLayout *vh = new QVBoxLayout( streamFrame ); + QLabel *iconLabel = new QLabel(content); + iconLabel->setPixmap(QPixmap::fromImage(thumb)); + vh->addWidget(iconLabel); + if (audio_list.count() > 1) { + QComboBox *cb = new QComboBox(content); + for (int k = 0; k < audio_list.count(); k++) { + cb->addItem(i18n("Audio stream %1", audio_list.at(k)), audio_list.at(k)); + } + comboList << cb; + cb->setCurrentIndex(j); + vh->addWidget(cb); + } + vbox->addWidget(streamFrame); + } + if (dialog.exec() == QDialog::Accepted) { + // import selected streams + for (int i = 0; i < groupList.count(); i++) { + if (groupList.at(i)->isChecked()) { + int vindex = groupList.at(i)->property("vindex").toInt(); + int aindex = comboList.at(i)->itemData(comboList.at(i)->currentIndex()).toInt(); + data.insert("video_index", QString::number(vindex)); + data.insert("audio_index", QString::number(aindex)); + data.insert("bypassDuplicate", "1"); + emit addClip(KUrl(path), data); + } + } + } +} #include "renderer.moc" diff --git a/src/renderer.h b/src/renderer.h index 0bd82f92..201e353b 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -393,6 +393,8 @@ private slots: void slotOsdTimeout(); /** @brief Process the clip info requests (in a separate thread). */ void processFileProperties(); + /** @brief A clip with multiple video streams was found, ask what to do. */ + void slotMultiStreamProducerFound(const QString path, QList audio_list, QList video_list, stringMap data); signals: @@ -427,6 +429,8 @@ signals: void refreshDocumentProducers(bool displayRatioChanged, bool fpsChanged); /** @brief A proxy clip is missing, ask for creation. */ void requestProxy(QString); + /** @brief A multiple stream clip was found. */ + void multiStreamFound(const QString &,QList,QList,stringMap data); /** @brief A frame's image has to be shown. @@ -434,6 +438,7 @@ signals: * Used in Mac OS X. */ void showImageSignal(QImage); void showAudioSignal(const QByteArray &); + void addClip(const KUrl &, stringMap); public slots: diff --git a/src/utils/resourcewidget.cpp b/src/utils/resourcewidget.cpp index 002d3ec1..e82b5143 100644 --- a/src/utils/resourcewidget.cpp +++ b/src/utils/resourcewidget.cpp @@ -262,7 +262,7 @@ void ResourceWidget::slotGotFile(KJob *job) //res.setProperty( Soprano::Vocabulary::NAO::description(), #endif #endif - emit addClip(filePath, QString()); + emit addClip(filePath); } void ResourceWidget::slotOpenUrl(const QString &url) diff --git a/src/utils/resourcewidget.h b/src/utils/resourcewidget.h index 614538da..8d312e73 100644 --- a/src/utils/resourcewidget.h +++ b/src/utils/resourcewidget.h @@ -84,7 +84,7 @@ private: void updateLayout(); signals: - void addClip(KUrl, const QString &); + void addClip(KUrl, QMap data = QMap ()); }; -- 2.39.2