diff --git a/src/monitor/monitorproxy.cpp b/src/monitor/monitorproxy.cpp
index 0df165a78..973919c4a 100644
--- a/src/monitor/monitorproxy.cpp
+++ b/src/monitor/monitorproxy.cpp
@@ -1,317 +1,317 @@
/***************************************************************************
* Copyright (C) 2018 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* This file is part of Kdenlive. See www.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) version 3 or any later version *
* accepted by the membership of KDE e.V. (or its successor approved *
* by the membership of KDE e.V.), which shall act as a proxy *
* defined in Section 14 of version 3 of the license. *
* *
* 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, see . *
***************************************************************************/
#include "monitorproxy.h"
#include "core.h"
#include "doc/kthumb.h"
#include "glwidget.h"
#include "kdenlivesettings.h"
#include "monitormanager.h"
#include "profiles/profilemodel.hpp"
#include
#include
#include
#include
#define SEEK_INACTIVE (-1)
MonitorProxy::MonitorProxy(GLWidget *parent)
: QObject(parent)
, q(parent)
, m_position(0)
, m_seekPosition(-1)
, m_zoneIn(0)
, m_zoneOut(-1)
, m_hasAV(false)
, m_clipType(0)
{
}
int MonitorProxy::seekPosition() const
{
return m_seekPosition;
}
bool MonitorProxy::seeking() const
{
return m_seekPosition != SEEK_INACTIVE;
}
int MonitorProxy::position() const
{
return m_position;
}
int MonitorProxy::rulerHeight() const
{
return q->m_rulerHeight;
}
int MonitorProxy::overlayType() const
{
return (q->m_id == (int)Kdenlive::ClipMonitor ? KdenliveSettings::clipMonitorOverlayGuides() : KdenliveSettings::projectMonitorOverlayGuides());
}
void MonitorProxy::setOverlayType(int ix)
{
if (q->m_id == (int)Kdenlive::ClipMonitor) {
KdenliveSettings::setClipMonitorOverlayGuides(ix);
} else {
KdenliveSettings::setProjectMonitorOverlayGuides(ix);
}
}
QString MonitorProxy::markerComment() const
{
return m_markerComment;
}
void MonitorProxy::requestSeekPosition(int pos)
{
q->activateMonitor();
m_seekPosition = pos;
emit seekPositionChanged();
emit seekRequestChanged();
}
int MonitorProxy::seekOrCurrentPosition() const
{
return m_seekPosition == SEEK_INACTIVE ? m_position : m_seekPosition;
}
bool MonitorProxy::setPosition(int pos)
{
if (m_seekPosition == pos) {
m_position = pos;
m_seekPosition = SEEK_INACTIVE;
emit seekPositionChanged();
} else if (m_position == pos) {
return true;
} else {
m_position = pos;
}
emit positionChanged();
return false;
}
void MonitorProxy::setMarkerComment(const QString &comment)
{
if (m_markerComment == comment) {
return;
}
m_markerComment = comment;
emit markerCommentChanged();
}
void MonitorProxy::setSeekPosition(int pos)
{
m_seekPosition = pos;
emit seekPositionChanged();
}
void MonitorProxy::pauseAndSeek(int pos)
{
q->switchPlay(false);
requestSeekPosition(pos);
}
int MonitorProxy::zoneIn() const
{
return m_zoneIn;
}
int MonitorProxy::zoneOut() const
{
return m_zoneOut;
}
void MonitorProxy::setZoneIn(int pos)
{
if (m_zoneIn > 0) {
emit removeSnap(m_zoneIn);
}
m_zoneIn = pos;
if (pos > 0) {
emit addSnap(pos);
}
emit zoneChanged();
emit saveZone();
}
void MonitorProxy::setZoneOut(int pos)
{
if (m_zoneOut > 0) {
emit removeSnap(m_zoneOut - 1);
}
m_zoneOut = pos;
if (pos > 0) {
emit addSnap(pos - 1);
}
emit zoneChanged();
emit saveZone();
}
void MonitorProxy::setZone(int in, int out, bool sendUpdate)
{
if (m_zoneIn > 0) {
emit removeSnap(m_zoneIn);
}
if (m_zoneOut > 0) {
emit removeSnap(m_zoneOut - 1);
}
m_zoneIn = in;
m_zoneOut = out;
if (m_zoneIn > 0) {
emit addSnap(m_zoneIn);
}
if (m_zoneOut > 0) {
emit addSnap(m_zoneOut - 1);
}
emit zoneChanged();
if (sendUpdate) {
emit saveZone();
}
}
void MonitorProxy::setZone(QPoint zone, bool sendUpdate)
{
setZone(zone.x(), zone.y(), sendUpdate);
}
void MonitorProxy::resetZone()
{
m_zoneIn = 0;
m_zoneOut = -1;
}
double MonitorProxy::fps() const
{
return pCore->getCurrentFps();
}
QPoint MonitorProxy::zone() const
{
return {m_zoneIn, m_zoneOut};
}
QImage MonitorProxy::extractFrame(int frame_position, const QString &path, int width, int height, bool useSourceProfile)
{
if (width == -1) {
width = pCore->getCurrentProfile()->width();
height = pCore->getCurrentProfile()->height();
} else if (width % 2 == 1) {
width++;
}
if (!path.isEmpty()) {
QScopedPointer producer(new Mlt::Producer(pCore->getCurrentProfile()->profile(), path.toUtf8().constData()));
if (producer && producer->is_valid()) {
QImage img = KThumb::getFrame(producer.data(), frame_position, width, height);
return img;
}
}
if ((q->m_producer == nullptr) || !path.isEmpty()) {
QImage pix(width, height, QImage::Format_RGB32);
pix.fill(Qt::black);
return pix;
}
Mlt::Frame *frame = nullptr;
QImage img;
if (useSourceProfile) {
// Our source clip's resolution is higher than current profile, export at full res
QScopedPointer tmpProfile(new Mlt::Profile());
QString service = q->m_producer->get("mlt_service");
QScopedPointer tmpProd(new Mlt::Producer(*tmpProfile, service.toUtf8().constData(), q->m_producer->get("resource")));
tmpProfile->from_producer(*tmpProd);
width = tmpProfile->width();
height = tmpProfile->height();
if (tmpProd && tmpProd->is_valid()) {
Mlt::Filter scaler(*tmpProfile, "swscale");
Mlt::Filter converter(*tmpProfile, "avcolor_space");
tmpProd->attach(scaler);
tmpProd->attach(converter);
// TODO: paste effects
// Clip(*tmpProd).addEffects(*q->m_producer);
double projectFps = pCore->getCurrentFps();
double currentFps = tmpProfile->fps();
if (qFuzzyCompare(projectFps, currentFps)) {
tmpProd->seek(q->m_producer->position());
} else {
tmpProd->seek(q->m_producer->position() * currentFps / projectFps);
}
frame = tmpProd->get_frame();
img = KThumb::getFrame(frame, width, height);
delete frame;
}
} else if (KdenliveSettings::gpu_accel()) {
QString service = q->m_producer->get("mlt_service");
QScopedPointer tmpProd(
new Mlt::Producer(pCore->getCurrentProfile()->profile(), service.toUtf8().constData(), q->m_producer->get("resource")));
Mlt::Filter scaler(pCore->getCurrentProfile()->profile(), "swscale");
Mlt::Filter converter(pCore->getCurrentProfile()->profile(), "avcolor_space");
tmpProd->attach(scaler);
tmpProd->attach(converter);
tmpProd->seek(q->m_producer->position());
frame = tmpProd->get_frame();
img = KThumb::getFrame(frame, width, height);
delete frame;
} else {
frame = q->m_producer->get_frame();
img = KThumb::getFrame(frame, width, height);
delete frame;
}
return img;
}
void MonitorProxy::activateClipMonitor(bool isClipMonitor)
{
pCore->monitorManager()->activateMonitor(isClipMonitor ? Kdenlive::ClipMonitor : Kdenlive::ProjectMonitor);
}
QString MonitorProxy::toTimecode(int frames) const
{
return KdenliveSettings::frametimecode() ? QString::number(frames) : q->frameToTime(frames);
}
void MonitorProxy::setClipProperties(ClipType::ProducerType type, bool hasAV, const QString clipName)
{
if (hasAV != m_hasAV) {
m_hasAV = hasAV;
emit clipHasAVChanged();
}
if (clipName == m_clipName) {
m_clipName.clear();
emit clipNameChanged();
}
m_clipName = clipName;
emit clipNameChanged();
if (type != m_clipType) {
m_clipType = type;
emit clipTypeChanged();
}
}
-void MonitorProxy::setAudioThumb(const QString thumbPath)
+void MonitorProxy::setAudioThumb(const QUrl thumbPath)
{
m_audioThumb = thumbPath;
emit audioThumbChanged();
}
diff --git a/src/monitor/monitorproxy.h b/src/monitor/monitorproxy.h
index f3ca9b6ec..e5497ef05 100644
--- a/src/monitor/monitorproxy.h
+++ b/src/monitor/monitorproxy.h
@@ -1,130 +1,131 @@
/***************************************************************************
* Copyright (C) 2018 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* This file is part of Kdenlive. See www.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) version 3 or any later version *
* accepted by the membership of KDE e.V. (or its successor approved *
* by the membership of KDE e.V.), which shall act as a proxy *
* defined in Section 14 of version 3 of the license. *
* *
* 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, see . *
***************************************************************************/
/** @brief This class is a wrapper around the monitor / glwidget and handles communication
* with the qml overlay through its properties.
*/
#ifndef MONITORPROXY_H
#define MONITORPROXY_H
#include "definitions.h"
#include
+#include
#include
class GLWidget;
class MonitorProxy : public QObject
{
Q_OBJECT
// Q_PROPERTY(int consumerPosition READ consumerPosition NOTIFY consumerPositionChanged)
Q_PROPERTY(int position READ position NOTIFY positionChanged)
Q_PROPERTY(int seekPosition READ seekPosition WRITE setSeekPosition NOTIFY seekPositionChanged)
Q_PROPERTY(int zoneIn READ zoneIn WRITE setZoneIn NOTIFY zoneChanged)
Q_PROPERTY(int zoneOut READ zoneOut WRITE setZoneOut NOTIFY zoneChanged)
Q_PROPERTY(int rulerHeight READ rulerHeight NOTIFY rulerHeightChanged)
Q_PROPERTY(QString markerComment READ markerComment NOTIFY markerCommentChanged)
- Q_PROPERTY(QString audioThumb MEMBER m_audioThumb NOTIFY audioThumbChanged)
+ Q_PROPERTY(QUrl audioThumb MEMBER m_audioThumb NOTIFY audioThumbChanged)
Q_PROPERTY(int overlayType READ overlayType WRITE setOverlayType NOTIFY overlayTypeChanged)
/** @brief: Returns true if current clip in monitor has Audio and Video
* */
Q_PROPERTY(bool clipHasAV MEMBER m_hasAV NOTIFY clipHasAVChanged)
/** @brief: Contains the name of clip currently displayed in monitor
* */
Q_PROPERTY(QString clipName MEMBER m_clipName NOTIFY clipNameChanged)
/** @brief: Contains the name of clip currently displayed in monitor
* */
Q_PROPERTY(int clipType MEMBER m_clipType NOTIFY clipTypeChanged)
public:
MonitorProxy(GLWidget *parent);
int seekPosition() const;
/** brief: Returns true if we are still in a seek operation
* */
bool seeking() const;
int position() const;
int rulerHeight() const;
int overlayType() const;
void setOverlayType(int ix);
QString markerComment() const;
Q_INVOKABLE void requestSeekPosition(int pos);
/** brief: Returns seek position or consumer position when not seeking
* */
int seekOrCurrentPosition() const;
/** brief: update position and end seeking if we reached the requested seek position.
* returns true if the position was unchanged, false otherwise
* */
bool setPosition(int pos);
void setMarkerComment(const QString &comment);
void setSeekPosition(int pos);
void pauseAndSeek(int pos);
int zoneIn() const;
int zoneOut() const;
void setZoneIn(int pos);
void setZoneOut(int pos);
Q_INVOKABLE void setZone(int in, int out, bool sendUpdate = true);
/** brief: Activate clip monitor if param is true, project monitor otherwise
* */
Q_INVOKABLE void activateClipMonitor(bool isClipMonitor);
void setZone(QPoint zone, bool sendUpdate = true);
void resetZone();
QPoint zone() const;
QImage extractFrame(int frame_position, const QString &path = QString(), int width = -1, int height = -1, bool useSourceProfile = false);
Q_INVOKABLE QString toTimecode(int frames) const;
Q_INVOKABLE double fps() const;
void setClipProperties(ClipType::ProducerType type, bool hasAV, const QString clipName);
- void setAudioThumb(const QString thumbPath = QString());
+ void setAudioThumb(const QUrl thumbPath = QUrl());
signals:
void positionChanged();
void seekPositionChanged();
void seekRequestChanged();
void zoneChanged();
void saveZone();
void markerCommentChanged();
void rulerHeightChanged();
void addSnap(int);
void removeSnap(int);
void triggerAction(const QString &name);
void overlayTypeChanged();
void seekNextKeyframe();
void seekPreviousKeyframe();
void addRemoveKeyframe();
void seekToKeyframe();
void clipHasAVChanged();
void clipNameChanged();
void clipTypeChanged();
void audioThumbChanged();
private:
GLWidget *q;
int m_position;
int m_seekPosition;
int m_zoneIn;
int m_zoneOut;
bool m_hasAV;
- QString m_audioThumb;
+ QUrl m_audioThumb;
QString m_markerComment;
QString m_clipName;
int m_clipType;
};
#endif
diff --git a/src/utils/thumbnailcache.cpp b/src/utils/thumbnailcache.cpp
index 2f34375d1..857ba56dc 100644
--- a/src/utils/thumbnailcache.cpp
+++ b/src/utils/thumbnailcache.cpp
@@ -1,271 +1,271 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.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) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* 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, see . *
***************************************************************************/
#include "thumbnailcache.hpp"
#include "bin/projectclip.h"
#include "bin/projectitemmodel.h"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include
#include
#include
std::unique_ptr ThumbnailCache::instance;
std::once_flag ThumbnailCache::m_onceFlag;
class ThumbnailCache::Cache_t
{
public:
Cache_t(int maxCost)
: m_maxCost(maxCost)
{
}
bool contains(const QString &key) const { return m_cache.count(key) > 0; }
void remove(const QString &key)
{
if (!contains(key)) {
return;
}
auto it = m_cache.at(key);
m_currentCost -= (*it).second.second;
m_data.erase(it);
m_cache.erase(key);
}
void insert(const QString &key, const QImage &img, int cost)
{
if (cost > m_maxCost) {
return;
}
m_data.push_front({key, {img, cost}});
auto it = m_data.begin();
m_cache[key] = it;
m_currentCost += cost;
while (m_currentCost > m_maxCost) {
remove(m_data.back().first);
}
}
QImage get(const QString &key)
{
if (!contains(key)) {
return QImage();
}
// when a get operation occurs, we put the corresponding list item in front to remember last access
std::pair> data;
auto it = m_cache.at(key);
std::swap(data, (*it)); // take data out without copy
QImage result = data.second.first; // a copy occurs here
m_data.erase(it); // delete old iterator
m_cache[key] = m_data.emplace(m_data.begin(), std::move(data)); // reinsert without copy and store iterator
return result;
}
protected:
int m_maxCost;
int m_currentCost{0};
std::list>> m_data; // the data is stored as (key,(image, cost))
std::unordered_map m_cache;
};
ThumbnailCache::ThumbnailCache()
: m_volatileCache(new Cache_t(10000000))
{
}
std::unique_ptr &ThumbnailCache::get()
{
std::call_once(m_onceFlag, [] { instance.reset(new ThumbnailCache()); });
return instance;
}
bool ThumbnailCache::hasThumbnail(const QString &binId, int pos, bool volatileOnly) const
{
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = pos < 0 ? getAudioKey(binId, &ok) : getKey(binId, pos, &ok);
if (ok && m_volatileCache->contains(key)) {
return true;
}
if (!ok || volatileOnly) {
return false;
}
QDir thumbFolder = getDir(pos < 0, &ok);
return ok && thumbFolder.exists(key);
}
QImage ThumbnailCache::getAudioThumbnail(const QString &binId, bool volatileOnly) const
{
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = getAudioKey(binId, &ok);
if (ok && m_volatileCache->contains(key)) {
return m_volatileCache->get(key);
}
if (!ok || volatileOnly) {
return QImage();
}
QDir thumbFolder = getDir(true, &ok);
if (ok && thumbFolder.exists(key)) {
m_storedOnDisk[binId].push_back(-1);
return QImage(thumbFolder.absoluteFilePath(key));
}
return QImage();
}
-const QString ThumbnailCache::getAudioThumbPath(const QString &binId) const
+const QUrl ThumbnailCache::getAudioThumbPath(const QString &binId) const
{
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = getAudioKey(binId, &ok);
QDir thumbFolder = getDir(true, &ok);
if (ok && thumbFolder.exists(key)) {
- return QStringLiteral("file://") + thumbFolder.absoluteFilePath(key);
+ return QUrl::fromLocalFile(thumbFolder.absoluteFilePath(key));
}
- return QString();
+ return QUrl();
}
QImage ThumbnailCache::getThumbnail(const QString &binId, int pos, bool volatileOnly) const
{
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = getKey(binId, pos, &ok);
if (ok && m_volatileCache->contains(key)) {
return m_volatileCache->get(key);
}
if (!ok || volatileOnly) {
return QImage();
}
QDir thumbFolder = getDir(false, &ok);
if (ok && thumbFolder.exists(key)) {
m_storedOnDisk[binId].push_back(pos);
return QImage(thumbFolder.absoluteFilePath(key));
}
return QImage();
}
void ThumbnailCache::storeThumbnail(const QString &binId, int pos, const QImage &img, bool persistent)
{
QMutexLocker locker(&m_mutex);
bool ok = false;
const QString key = getKey(binId, pos, &ok);
if (!ok) {
return;
}
if (persistent) {
QDir thumbFolder = getDir(false, &ok);
if (ok) {
if (!img.save(thumbFolder.absoluteFilePath(key))) {
qDebug() << ".............\nAAAAAAAAAAAARGH ERROR SAVING THUMB";
}
m_storedOnDisk[binId].push_back(pos);
// if volatile cache also contains this entry, update it
if (m_volatileCache->contains(key)) {
m_volatileCache->remove(key);
} else {
m_storedVolatile[binId].push_back(pos);
}
m_volatileCache->insert(key, img, (int)img.sizeInBytes());
}
} else {
m_volatileCache->insert(key, img, (int)img.sizeInBytes());
m_storedVolatile[binId].push_back(pos);
}
}
void ThumbnailCache::saveCachedThumbs(QStringList keys)
{
bool ok;
QDir thumbFolder = getDir(false, &ok);
if (!ok) {
return;
}
for (const QString &key : keys) {
if (!thumbFolder.exists(key) && m_volatileCache->contains(key)) {
QImage img = m_volatileCache->get(key);
if (!img.save(thumbFolder.absoluteFilePath(key))) {
qDebug() << "// Error writing thumbnails to " << thumbFolder.absolutePath();
break;
}
}
}
}
void ThumbnailCache::invalidateThumbsForClip(const QString &binId)
{
QMutexLocker locker(&m_mutex);
if (m_storedVolatile.find(binId) != m_storedVolatile.end()) {
bool ok = false;
for (int pos : m_storedVolatile.at(binId)) {
auto key = getKey(binId, pos, &ok);
if (ok) {
m_volatileCache->remove(key);
}
}
m_storedVolatile.erase(binId);
}
bool ok = false;
// Video thumbs
QDir thumbFolder = getDir(false, &ok);
QDir audioThumbFolder = getDir(true, &ok);
if (ok && m_storedOnDisk.find(binId) != m_storedOnDisk.end()) {
// Remove persistent cache
for (int pos : m_storedOnDisk.at(binId)) {
if (pos < 0) {
auto key = getAudioKey(binId, &ok);
if (ok) {
QFile::remove(audioThumbFolder.absoluteFilePath(key));
}
} else {
auto key = getKey(binId, pos, &ok);
if (ok) {
QFile::remove(thumbFolder.absoluteFilePath(key));
}
}
}
m_storedOnDisk.erase(binId);
}
}
// static
QString ThumbnailCache::getKey(const QString &binId, int pos, bool *ok)
{
auto binClip = pCore->projectItemModel()->getClipByBinID(binId);
*ok = binClip != nullptr;
return *ok ? binClip->hash() + QLatin1Char('#') + QString::number(pos) + QStringLiteral(".png") : QString();
}
// static
QString ThumbnailCache::getAudioKey(const QString &binId, bool *ok)
{
auto binClip = pCore->projectItemModel()->getClipByBinID(binId);
*ok = binClip != nullptr;
return *ok ? binClip->hash() + QStringLiteral(".png") : QString();
}
// static
QDir ThumbnailCache::getDir(bool audio, bool *ok)
{
return pCore->currentDoc()->getCacheDir(audio ? CacheAudio : CacheThumbs, ok);
}
diff --git a/src/utils/thumbnailcache.hpp b/src/utils/thumbnailcache.hpp
index c60e0af5f..76b379140 100644
--- a/src/utils/thumbnailcache.hpp
+++ b/src/utils/thumbnailcache.hpp
@@ -1,100 +1,101 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.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) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* 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, see . *
***************************************************************************/
#pragma once
#include "definitions.h"
#include
+#include
#include
#include
#include
#include
#include
#include
/** @brief This class class is an interface to the caches that store thumbnails.
In Kdenlive, we use two such caches, a persistent that is stored on disk to allow thumbnails to be reused when reopening.
The other one is a volatile LRU cache that lives in memory.
Note that for the volatile cache uses a custom implementation.
QCache is not suitable since it operates on pointers and since the object is removed from the cache when accessed.
KImageCache is not suitable since it lacks a way to remove objects from the cache.
* Note that this class is a Singleton
*/
class ThumbnailCache
{
public:
// Returns the instance of the Singleton
static std::unique_ptr &get();
/* @brief Check whether a given thumbnail is in the cache
@param binId is the id of the queried clip
@param pos is the position where we query
@param volatileOnly if true, we only check the volatile cache (no disk access)
*/
bool hasThumbnail(const QString &binId, int pos, bool volatileOnly = false) const;
/* @brief Get a given thumbnail from the cache
@param binId is the id of the queried clip
@param pos is the position where we query
@param volatileOnly if true, we only check the volatile cache (no disk access)
*/
QImage getThumbnail(const QString &binId, int pos, bool volatileOnly = false) const;
QImage getAudioThumbnail(const QString &binId, bool volatileOnly = false) const;
- const QString getAudioThumbPath(const QString &binId) const;
+ const QUrl getAudioThumbPath(const QString &binId) const;
/* @brief Get a given thumbnail from the cache
@param binId is the id of the queried clip
@param pos is the position where we query
@param persistent if true, we store the image in the persistent cache, which generates a disk access
*/
void storeThumbnail(const QString &binId, int pos, const QImage &img, bool persistent = false);
/* @brief Removes all the thumbnails for a given clip */
void invalidateThumbsForClip(const QString &binId);
/* @brief Save all cached thumbs to disk */
void saveCachedThumbs(QStringList keys);
protected:
// Constructor is protected because class is a Singleton
ThumbnailCache();
// Return the key associated to a thumbnail
static QString getKey(const QString &binId, int pos, bool *ok);
static QString getAudioKey(const QString &binId, bool *ok);
// Return the dir where the persistent cache lives
static QDir getDir(bool audio, bool *ok);
static std::unique_ptr instance;
static std::once_flag m_onceFlag; // flag to create the repository only once;
class Cache_t;
std::unique_ptr m_volatileCache;
mutable QMutex m_mutex;
// the following maps keeps track of the positions that we store for each clip in volatile caches.
// Note that we don't track deletions due to items dropped from the cache. So the maps can contain more items that are currently stored.
std::unordered_map> m_storedVolatile;
mutable std::unordered_map> m_storedOnDisk;
};