Fix video thumbnails from previous project showing up.

Various crash fixes on exit or project close.
BUG: 414813
parent ba4adf7c
......@@ -849,6 +849,7 @@ Bin::Bin(std::shared_ptr<ProjectItemModel> model, QWidget *parent)
Bin::~Bin()
{
pCore->jobManager()->slotCancelJobs();
blockSignals(true);
m_proxyModel->selectionModel()->blockSignals(true);
setEnabled(false);
......@@ -1181,6 +1182,7 @@ void Bin::setDocument(KdenliveDoc *project)
delete m_itemView;
m_itemView = nullptr;
m_doc = project;
m_infoLabel->slotSetJobCount(0);
int iconHeight = QFontInfo(font()).pixelSize() * 3.5;
m_iconSize = QSize(iconHeight * pCore->getCurrentDar(), iconHeight);
setEnabled(true);
......
......@@ -185,7 +185,11 @@ bool AudioThumbJob::computeWithFFMPEG()
}
m_ffmpegProcess.reset(new QProcess);
connect(m_ffmpegProcess.get(), &QProcess::readyReadStandardOutput, this, &AudioThumbJob::updateFfmpegProgress, Qt::UniqueConnection);
connect(this, &AudioThumbJob::jobCanceled, m_ffmpegProcess.get(), &QProcess::kill, Qt::DirectConnection);
connect(this, &AudioThumbJob::jobCanceled, [&]() {
if (m_ffmpegProcess) {
m_ffmpegProcess->kill();
}
});
m_ffmpegProcess->start(KdenliveSettings::ffmpegpath(), args);
m_ffmpegProcess->waitForFinished(-1);
if (m_ffmpegProcess->exitStatus() != QProcess::CrashExit) {
......
......@@ -51,8 +51,8 @@ CacheJob::CacheJob(const QString &binId, int imageHeight, int thumbsCount, int i
auto item = pCore->projectItemModel()->getItemByBinId(binId);
Q_ASSERT(item != nullptr && item->itemType() == AbstractProjectItem::ClipItem);
connect(this, &CacheJob::jobCanceled, [&] () {
m_clipId.clear();
m_done = true;
m_clipId.clear();
});
}
......@@ -103,7 +103,7 @@ bool CacheJob::startJob()
frame->set("deinterlace_method", "onefield");
frame->set("top_field_first", -1);
frame->set("rescale.interp", "nearest");
if (!m_done && (frame != nullptr) && frame->is_valid()) {
if (frame != nullptr && frame->is_valid()) {
QImage result = KThumb::getFrame(frame.data());
ThumbnailCache::get()->storeThumbnail(m_clipId, i, result, true);
}
......
......@@ -212,6 +212,7 @@ void JobManager::slotCancelJobs()
{
QWriteLocker locker(&m_lock);
for (const auto &j : m_jobs) {
j.second->m_processed = true;
for (const std::shared_ptr<AbstractClipJob> &job : j.second->m_job) {
job->jobCanceled();
}
......@@ -249,7 +250,9 @@ void JobManager::createJob(const std::shared_ptr<Job_t> &job)
auto binId = it.first;
connect(job->m_job[i].get(), &AbstractClipJob::jobProgress, [job, i, binId](int p) {
job->m_progress[i] = std::max(job->m_progress[i], p);
pCore->projectItemModel()->onItemUpdated(binId, AbstractProjectItem::JobProgress);
if (pCore) {
pCore->projectItemModel()->onItemUpdated(binId, AbstractProjectItem::JobProgress);
}
});
}
connect(&job->m_future, &QFutureWatcher<bool>::started, this, &JobManager::updateJobCount);
......@@ -367,7 +370,9 @@ void JobManager::slotManageFinishedJob(int id)
if (m_jobsByParents.count(id) > 0) {
std::vector<int> children = m_jobsByParents[id];
for (int cid : children) {
QtConcurrent::run(this, &JobManager::createJob, m_jobs[cid]);
if (!m_jobs[cid]->m_processed) {
QtConcurrent::run(this, &JobManager::createJob, m_jobs[cid]);
}
}
m_jobsByParents.erase(id);
}
......
......@@ -246,6 +246,7 @@ bool MeltJob::startJob()
}
m_showFrameEvent.reset(m_consumer->listen("consumer-frame-show", this, (mlt_listener)consumer_frame_render));
connect(this, &MeltJob::jobCanceled, [&] () {
m_showFrameEvent.reset();
m_consumer->stop();
return false;
});
......
......@@ -197,6 +197,7 @@ void ProjectManager::newFile(QString profileName, bool showProjectSettings)
KdenliveDoc *doc = new KdenliveDoc(QUrl(), projectFolder, pCore->window()->m_commandStack, profileName, documentProperties, documentMetadata, projectTracks,
&openBackup, pCore->window());
doc->m_autosave = new KAutoSaveFile(startFile, doc);
ThumbnailCache::get()->clearCache();
pCore->bin()->setDocument(doc);
m_project = doc;
pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
......@@ -501,6 +502,7 @@ void ProjectManager::doOpenFile(const QUrl &url, KAutoSaveFile *stale)
m_fileRevert->setEnabled(true);
delete m_progressDialog;
ThumbnailCache::get()->clearCache();
pCore->monitorManager()->resetDisplay();
pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
m_progressDialog = new QProgressDialog(pCore->window());
......
......@@ -66,7 +66,6 @@ Rectangle {
property bool hasAudio
property bool canBeAudio
property bool canBeVideo
property string hash: 'ccc' //TODO
property double speed: 1.0
property color borderColor: 'black'
property bool forceReloadThumb
......@@ -207,9 +206,7 @@ Rectangle {
*/
property bool variableThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
property bool isImage: itemType == ProducerType.Image
property string baseThumbPath: variableThumbs ? '' : 'image://thumbnail/' + binId + '/' + (isImage ? '#0' : '#')
property string inThumbPath: (variableThumbs || isImage ) ? baseThumbPath : baseThumbPath + Math.floor(inPoint * speed)
property string outThumbPath: (variableThumbs || isImage ) ? baseThumbPath : baseThumbPath + Math.floor(outPoint * speed)
property string baseThumbPath: variableThumbs ? '' : 'image://thumbnail/' + binId + '/' + documentId + '/' + (isImage ? '#0' : '#')
DropArea { //Drop area for clips
anchors.fill: clipRoot
......
......@@ -15,8 +15,8 @@ Row {
property bool enableCache: clipRoot.itemType == ProducerType.Video || clipRoot.itemType == ProducerType.AV
function reload() {
clipRoot.baseThumbPath =''
clipRoot.baseThumbPath = clipRoot.variableThumbs ? '' : 'image://thumbnail/' + clipRoot.binId + '/' + (clipRoot.isImage ? '#0' : '#')
console.log('+++++\n\ntriggered ML thumb reload\n\n++++++++++++++')
clipRoot.baseThumbPath = clipRoot.variableThumbs ? '' : 'image://thumbnail/' + clipRoot.binId + '/' + Math.random() + '/' + (clipRoot.isImage ? '#0' : '#')
}
Repeater {
......
......@@ -36,11 +36,6 @@ ThumbnailProvider::ThumbnailProvider()
ThumbnailProvider::~ThumbnailProvider() = default;
void ThumbnailProvider::resetProject()
{
// m_producers.clear();
}
QImage ThumbnailProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
QImage result;
......
......@@ -32,7 +32,6 @@ public:
explicit ThumbnailProvider();
~ThumbnailProvider() override;
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
void resetProject();
private:
QString cacheKey(Mlt::Properties &properties, const QString &service, const QString &resource, const QString &hash, int frameNumber);
......
......@@ -47,6 +47,7 @@
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItem>
#include <QUuid>
#include <QSortFilterProxyModel>
const int TimelineWidget::comboScale[] = {1, 2, 4, 8, 15, 30, 50, 75, 100, 150, 200, 300, 500, 800, 1000, 1500, 2000, 3000, 6000, 15000, 30000};
......@@ -111,7 +112,6 @@ const QStringList TimelineWidget::sortedItems(const QStringList &items, bool isT
void TimelineWidget::setModel(const std::shared_ptr<TimelineItemModel> &model, MonitorProxy *proxy)
{
m_thumbnailer->resetProject();
m_sortModel = std::make_unique<QSortFilterProxyModel>(this);
m_sortModel->setSourceModel(model.get());
m_sortModel->setSortRole(TimelineItemModel::SortRole);
......@@ -122,6 +122,9 @@ void TimelineWidget::setModel(const std::shared_ptr<TimelineItemModel> &model, M
rootContext()->setContextProperty("controller", model.get());
rootContext()->setContextProperty("timeline", m_proxy);
rootContext()->setContextProperty("proxy", proxy);
// Create a unique id for this timeline to prevent thumbnails
// leaking from one project to another because of qml's image caching
rootContext()->setContextProperty("documentId", QUuid::createUuid());
rootContext()->setContextProperty("transitionModel", sortedItems(KdenliveSettings::favorite_transitions(), true)); // m_transitionProxyModel.get());
// rootContext()->setContextProperty("effectModel", m_effectsProxyModel.get());
rootContext()->setContextProperty("effectModel", sortedItems(KdenliveSettings::favorite_effects(), false));
......
......@@ -80,6 +80,12 @@ public:
m_cache[key] = m_data.emplace(m_data.begin(), std::move(data)); // reinsert without copy and store iterator
return result;
}
void clear()
{
m_data.clear();
m_cache.clear();
m_currentCost = 0;
}
protected:
int m_maxCost;
......@@ -247,12 +253,24 @@ void ThumbnailCache::invalidateThumbsForClip(const QString &binId, bool reloadAu
}
}
m_storedOnDisk.erase(binId);
}
}
}
void ThumbnailCache::clearCache()
{
QMutexLocker locker(&m_mutex);
m_volatileCache->clear();
m_storedVolatile.clear();
m_storedOnDisk.clear();
}
// static
QString ThumbnailCache::getKey(const QString &binId, int pos, bool *ok)
{
if (binId.isEmpty()) {
*ok = false;
return QString();
}
auto binClip = pCore->projectItemModel()->getClipByBinID(binId);
*ok = binClip != nullptr;
return *ok ? binClip->hash() + QLatin1Char('#') + QString::number(pos) + QStringLiteral(".png") : QString();
......
......@@ -76,6 +76,9 @@ public:
/* @brief Save all cached thumbs to disk */
void saveCachedThumbs(QStringList keys);
/* @brief Reset cache (discarding all thumbs stored in memory) */
void clearCache();
protected:
// Constructor is protected because class is a Singleton
ThumbnailCache();
......
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