Commit d0a5fb9c authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Speedup loading of projects with timeline preview.

Related to #1266
parent e3b71237
......@@ -1195,3 +1195,8 @@ void Core::addBin(const QString &id)
const QString folderName = bin->setDocument(pCore->currentDoc(), id);
m_mainWindow->addBin(bin, folderName);
}
void Core::loadTimelinePreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enablePreview, Mlt::Playlist &playlist)
{
pCore->window()->getMainTimeline()->controller()->loadPreview(chunks, dirty, documentDate, enablePreview, playlist);
}
......@@ -22,7 +22,8 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <unordered_set>
#include "timecode.h"
#include "mlt++/MltProfile.h"
#include <mlt++/MltProfile.h>
#include <mlt++/MltPlaylist.h>
class Bin;
class DocUndoStack;
......@@ -271,6 +272,7 @@ public:
int getNewStuff(const QString &configFile);
/** @brief Get the frame size of the clip above a composition */
const QSize getCompositionSizeOnTrack(const ObjectId &id);
void loadTimelinePreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enablePreview, Mlt::Playlist &playlist);
private:
explicit Core();
......
......@@ -222,7 +222,7 @@ void ProjectManager::newFile(QString profileName, bool showProjectSettings)
pCore->bin()->setDocument(doc);
m_project = doc;
pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
updateTimeline(0);
updateTimeline(0, QString(), QString(), QDateTime(), 0);
pCore->window()->connectDocument();
pCore->mixer()->setModel(m_mainTimelineModel);
bool disabled = m_project->getDocumentProperty(QStringLiteral("disabletimelineeffects")) == QLatin1String("1");
......@@ -615,19 +615,15 @@ void ProjectManager::doOpenFile(const QUrl &url, KAutoSaveFile *stale)
// Set default target tracks to upper audio / lower video tracks
m_project = doc;
doc->loadDocumentGuides();
if (!updateTimeline(m_project->getDocumentProperty(QStringLiteral("position")).toInt())) {
m_project->loadDocumentGuides();
QDateTime documentDate = QFileInfo(m_project->url().toLocalFile()).lastModified();
if (!updateTimeline(m_project->getDocumentProperty(QStringLiteral("position")).toInt(), m_project->getDocumentProperty(QStringLiteral("previewchunks")), m_project->getDocumentProperty(QStringLiteral("dirtypreviewchunks")), documentDate, m_project->getDocumentProperty(QStringLiteral("disablepreview")).toInt())) {
delete m_progressDialog;
m_progressDialog = nullptr;
return;
}
pCore->window()->connectDocument();
pCore->mixer()->setModel(m_mainTimelineModel);
QDateTime documentDate = QFileInfo(m_project->url().toLocalFile()).lastModified();
pCore->window()->getMainTimeline()->controller()->loadPreview(m_project->getDocumentProperty(QStringLiteral("previewchunks")),
m_project->getDocumentProperty(QStringLiteral("dirtypreviewchunks")), documentDate,
m_project->getDocumentProperty(QStringLiteral("disablepreview")).toInt());
emit docOpened(m_project);
pCore->displayMessage(QString(), OperationCompletedMessage, 100);
if (openBackup) {
......@@ -929,7 +925,7 @@ void ProjectManager::slotMoveFinished(KJob *job)
}
}
bool ProjectManager::updateTimeline(int pos)
bool ProjectManager::updateTimeline(int pos, const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enablePreview)
{
pCore->taskManager.slotCancelJobs();
pCore->window()->getMainTimeline()->loading = true;
......@@ -960,7 +956,7 @@ bool ProjectManager::updateTimeline(int pos)
m_mainTimelineModel->addSnap(0);
pCore->window()->getMainTimeline()->setModel(m_mainTimelineModel, pCore->monitorManager()->projectMonitor()->getControllerProxy());
bool projectErrors = false;
if (!constructTimelineFromMelt(m_mainTimelineModel, tractor, m_progressDialog, m_project->modifiedDecimalPoint(), &projectErrors)) {
if (!constructTimelineFromMelt(m_mainTimelineModel, tractor, m_progressDialog, m_project->modifiedDecimalPoint(), chunks, dirty, documentDate, enablePreview, &projectErrors)) {
//TODO: act on project load failure
qDebug()<<"// Project failed to load!!";
}
......
......@@ -185,7 +185,7 @@ signals:
protected:
/** @brief Update the timeline according to the MLT XML */
bool updateTimeline(int pos);
bool updateTimeline(int pos, const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enablePreview);
private:
/** @brief checks if autoback files exists, recovers from it if user says yes, returns true if files were recovered. */
......
......@@ -36,7 +36,7 @@ bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline,
bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, int tid, Mlt::Playlist &track,
const std::unordered_map<QString, QString> &binIdCorresp, Fun &undo, Fun &redo, bool audioTrack, QString originalDecimalPoint, int playlist, QList<Mlt::Transition *> compositions, QProgressDialog *progressDialog = nullptr);
bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, Mlt::Tractor tractor, QProgressDialog *progressDialog, QString originalDecimalPoint, bool *projectErrors)
bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, Mlt::Tractor tractor, QProgressDialog *progressDialog, const QString &originalDecimalPoint, const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enablePreview, bool *projectErrors)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
......@@ -72,6 +72,10 @@ bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timelin
std::unique_ptr<Mlt::Producer> track(tractor.track(i));
QString playlist_name = track->get("id");
if (reserved_names.contains(playlist_name)) {
if (playlist_name == QLatin1String("timeline_preview")) {
Mlt::Playlist local_playlist(*track);
pCore->loadTimelinePreview(chunks, dirty, documentDate, enablePreview, local_playlist);
}
continue;
}
switch (track->type()) {
......
......@@ -8,12 +8,13 @@
#include <memory>
#include <mlt++/MltTractor.h>
#include <QtCore/QString>
#include <QDateTime>
class TimelineItemModel;
class QProgressDialog;
/** @brief This function can be used to construct a TimelineModel object from a Mlt object hierarchy
*/
bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, Mlt::Tractor mlt_timeline, QProgressDialog *progressDialog = nullptr, QString originalDecimalPoint = QString(), bool *projectErrors = nullptr);
bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timeline, Mlt::Tractor mlt_timeline, QProgressDialog *progressDialog = nullptr, const QString &originalDecimalPoint = QString(), const QString &chunks = QString(), const QString &dirty = QString(), const QDateTime &documentDate = QDateTime(), int enablePreview = 0, bool *projectErrors = nullptr);
#endif
......@@ -127,7 +127,7 @@ bool PreviewManager::buildPreviewTrack()
return true;
}
void PreviewManager::loadChunks(QVariantList previewChunks, QVariantList dirtyChunks, const QDateTime &documentDate)
void PreviewManager::loadChunks(QVariantList previewChunks, QVariantList dirtyChunks, const QDateTime &documentDate, Mlt::Playlist &playlist)
{
if (previewChunks.isEmpty()) {
previewChunks = m_renderedChunks;
......@@ -135,21 +135,39 @@ void PreviewManager::loadChunks(QVariantList previewChunks, QVariantList dirtyCh
if (dirtyChunks.isEmpty()) {
dirtyChunks = m_dirtyChunks;
}
for (const auto &frame : qAsConst(previewChunks)) {
const QString fileName = m_cacheDir.absoluteFilePath(QStringLiteral("%1.%2").arg(frame.toInt()).arg(m_extension));
QFile file(fileName);
if (file.exists()) {
if (!documentDate.isNull() && QFileInfo(file).lastModified() > documentDate) {
// Timeline preview file was created after document, invalidate
file.remove();
dirtyChunks << frame;
} else {
gotPreviewRender(frame.toInt(), fileName, 1000);
}
// First chech if there are invalid chunks (created after document date)
QFileInfoList chunksList = m_cacheDir.entryInfoList({QString("*.%1").arg(m_extension)}, QDir::Files, QDir::Time);
for (auto &chunkFile : chunksList) {
if (chunkFile.lastModified() > documentDate) {
// This chunk is invalid
QString chunkName = chunkFile.fileName().section(QLatin1Char('.'), 0, 0);
previewChunks.removeAll(chunkName);
dirtyChunks << chunkName;
// Physically remove chunk file
m_cacheDir.remove(chunkFile.fileName());
} else {
dirtyChunks << frame;
// Done
break;
}
}
int max = playlist.count();
std::shared_ptr<Mlt::Producer> clip;
m_tractor->lock();
for (int i = 0; i < max; i++) {
if (playlist.is_blank(i)) {
continue;
}
int position = playlist.clip_start(i);
if (previewChunks.contains(QString::number(position))) {
clip.reset(playlist.get_clip(i));
m_renderedChunks << position;
m_previewTrack->insert_at(position, clip.get(), 1);
}
}
m_previewTrack->consolidate_blanks();
m_tractor->unlock();
if (!previewChunks.isEmpty()) {
emit m_controller->renderedChunksChanged();
}
......
......@@ -65,7 +65,7 @@ public:
/** @brief: Returns directory currently used to store the preview files. */
const QDir getCacheDir() const;
/** @brief: Load existing ruler chunks. */
void loadChunks(QVariantList previewChunks, QVariantList dirtyChunks, const QDateTime &documentDate);
void loadChunks(QVariantList previewChunks, QVariantList dirtyChunks, const QDateTime &documentDate, Mlt::Playlist &playlist);
int setOverlayTrack(Mlt::Playlist *overlay);
/** @brief Remove the effect compare overlay track */
void removeOverlayTrack();
......
......@@ -2412,7 +2412,8 @@ void TimelineController::disablePreview(bool disable)
m_timelinePreview->reconnectTrack();
m_model->m_tractor->unlock();
}
m_timelinePreview->loadChunks(QVariantList(), QVariantList(), QDateTime());
Mlt::Playlist playlist;
m_timelinePreview->loadChunks(QVariantList(), QVariantList(), QDateTime(), playlist);
m_usePreview = true;
}
}
......@@ -2452,7 +2453,7 @@ void TimelineController::resetPreview()
}
}
void TimelineController::loadPreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enable)
void TimelineController::loadPreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enable, Mlt::Playlist &playlist)
{
if (chunks.isEmpty() && dirty.isEmpty()) {
return;
......@@ -2490,7 +2491,7 @@ void TimelineController::loadPreview(const QString &chunks, const QString &dirty
m_usePreview = true;
m_model->m_overlayTrackCount = m_timelinePreview->addedTracks();
}
m_timelinePreview->loadChunks(renderedChunks, dirtyChunks, documentDate);
m_timelinePreview->loadChunks(renderedChunks, dirtyChunks, documentDate, playlist);
}
}
......
......@@ -559,7 +559,7 @@ public:
/** @brief Load timeline preview from saved doc
*/
void loadPreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enable);
void loadPreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enable, Mlt::Playlist &playlist);
/** @brief Return document properties with added settings from timeline
*/
QMap<QString, QString> documentProperties();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment