Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Switch timeline thumbnails to use thumbnailCache, fix various issues with...

Switch timeline thumbnails to use thumbnailCache, fix various issues with timeline thumbs not updating correctly
parent 71c96f1b
......@@ -2743,7 +2743,7 @@ void Bin::showTitleWidget(std::shared_ptr<ProjectClip> clip)
if (dia_ui.exec() == QDialog::Accepted) {
QMap<QString, QString> newprops;
newprops.insert(QStringLiteral("xmldata"), dia_ui.xml().toString());
if (!qFuzzyCompare((double)dia_ui.duration(), clip->duration().frames(pCore->getCurrentFps()))) {
if (dia_ui.duration() != clip->duration().frames(pCore->getCurrentFps()) + 1) {
// duration changed, we need to update duration
newprops.insert(QStringLiteral("out"), QString::number(dia_ui.duration() - 1));
int currentLength = clip->getProducerIntProperty(QStringLiteral("kdenlive:duration"));
......
......@@ -112,7 +112,6 @@ void BinPlaylist::removeBinClip(const QString &id)
void BinPlaylist::changeProducer(const QString &id, const std::shared_ptr<Mlt::Producer> &producer)
{
Q_ASSERT(m_allClips.count(id) > 0);
qDebug() << "888888888 Changing producer"<<id;
removeBinClip(id);
m_binPlaylist->append(*producer.get());
}
......
......@@ -289,14 +289,18 @@ void ProjectClip::reloadProducer(bool refreshOnly)
if (refreshOnly) {
// In that case, we only want a new thumbnail.
// We thus set up a thumb job. We must make sure that there is no pending LOADJOB
pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadjobId, QString(), 150, -1, true);
// Clear cache first
ThumbnailCache::get()->invalidateThumbsForClip(clipId());
pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadjobId, QString(), 150, -1, true, true);
} else {
//TODO: check if another load job is running?
QDomDocument doc;
QDomElement xml = toXml(doc);
if (!xml.isNull()) {
pCore->jobManager()->startJob<LoadJob>({clipId()}, -1, QString(), xml);
ThumbnailCache::get()->invalidateThumbsForClip(clipId());
int loadJob = pCore->jobManager()->startJob<LoadJob>({clipId()}, -1, QString(), xml);
pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadJob, QString(), 150, -1, true, true);
}
}
}
......@@ -328,7 +332,6 @@ void ProjectClip::setThumbnail(const QImage &img)
p.drawText(r, Qt::AlignCenter, i18nc("The first letter of Proxy, used as abbreviation", "P"));
}
m_thumbnail = QIcon(thumb);
updateTimelineClips(QVector<int>() << TimelineModel::ReloadThumb);
if (auto ptr = m_model.lock()) {
std::static_pointer_cast<ProjectItemModel>(ptr)->onItemUpdated(std::static_pointer_cast<ProjectClip>(shared_from_this()), AbstractProjectItem::DataThumbnail);
}
......@@ -391,13 +394,14 @@ std::shared_ptr<Mlt::Producer> ProjectClip::thumbProducer()
}
Clip clip(*prod.get());
if (KdenliveSettings::gpu_accel()) {
//TODO: when the original producer changes, we must reload this thumb producer
m_thumbsProducer = std::make_shared<Mlt::Producer>(clip.softClone(ClipController::getPassPropertiesList()));
Mlt::Filter scaler(*prod->profile(), "swscale");
Mlt::Filter converter(*prod->profile(), "avcolor_space");
m_thumbsProducer->attach(scaler);
m_thumbsProducer->attach(converter);
} else {
m_thumbsProducer = std::make_shared<Mlt::Producer>(clip.clone());
m_thumbsProducer = std::make_shared<Mlt::Producer>(new Mlt::Producer(prod.get()));
}
return m_thumbsProducer;
}
......@@ -501,6 +505,7 @@ const QString ProjectClip::getFileHash()
fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
break;
case ClipType::Text:
case ClipType::TextTemplate:
fileData = getProducerProperty(QStringLiteral("xmldata")).toUtf8();
fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
break;
......@@ -534,6 +539,7 @@ const QString ProjectClip::getFileHash()
break;
}
if (fileHash.isEmpty()) {
qDebug()<<"// WARNING EMPTY CLIP HASH: ";
return QString();
}
QString result = fileHash.toHex();
......
......@@ -190,6 +190,7 @@ public:
QList<int> timelineInstances() const;
std::shared_ptr<Mlt::Producer> timelineProducer(PlaylistState::ClipState state = PlaylistState::Original, int track = 1);
std::shared_ptr<Mlt::Producer> cloneProducer();
void updateTimelineClips(QVector<int> roles);
protected:
friend class ClipModel;
......@@ -232,7 +233,6 @@ private:
QList<int> m_requestedThumbs;
const QString geometryWithOffset(const QString &data, int offset);
void doExtractImage();
void updateTimelineClips(QVector<int> roles);
std::map<int, std::weak_ptr<TimelineModel>> m_registeredClips;
std::map<int, std::shared_ptr<Mlt::Producer>> m_timelineProducers;
......
......@@ -32,12 +32,13 @@
#include <QScopedPointer>
#include <mlt++/MltProducer.h>
ThumbJob::ThumbJob(const QString &binId, int imageHeight, int frameNumber, bool persistent)
ThumbJob::ThumbJob(const QString &binId, int imageHeight, int frameNumber, bool persistent, bool reloadAllThumbs)
: AbstractClipJob(THUMBJOB, binId)
, m_frameNumber(frameNumber)
, m_fullWidth(imageHeight * pCore->getCurrentDar() + 0.5)
, m_imageHeight(imageHeight)
, m_persistent(persistent)
, m_reloadAll(reloadAllThumbs)
, m_subClip(false)
{
auto item = pCore->projectItemModel()->getItemByBinId(binId);
......@@ -77,8 +78,9 @@ bool ThumbJob::startJob()
if ((m_prod == nullptr) || !m_prod->is_valid()) {
return false;
}
m_inCache = false;
int max = m_prod->get_length();
m_frameNumber = std::min(max - 1, m_frameNumber);
m_frameNumber = m_binClip->clipType() == ClipType::Image ? 0 : qBound(0, m_frameNumber, max - 1);
// m_frameNumber = ProjectClip::getXmlProperty(info.xml, QStringLiteral("kdenlive:thumbnailFrame"), QStringLiteral("-1")).toInt();
if (ThumbnailCache::get()->hasThumbnail(m_binClip->clipId(), m_frameNumber, !m_persistent)) {
......@@ -113,6 +115,13 @@ bool ThumbJob::commitResult(Fun &undo, Fun &redo)
qDebug() << "ERROR: Trying to consume invalid results";
return false;
}
if (!m_inCache) {
if (m_result.isNull()) {
qDebug()<<"+++++\nINVALID RESULT IMAGE\n++++++++++++++";
} else {
ThumbnailCache::get()->storeThumbnail(m_binClip->clipId(), m_frameNumber, m_result, m_persistent);
}
}
m_resultConsumed = true;
// TODO a refactor of ProjectClip and ProjectSubClip should make that possible without branching (both classes implement setThumbnail)
......@@ -140,14 +149,20 @@ bool ThumbJob::commitResult(Fun &undo, Fun &redo)
QImage old = m_binClip->thumbnail(m_result.width(), m_result.height()).toImage();
// note that the image is moved into lambda, it won't be available from this class anymore
auto operation = [ clip = m_binClip, image = std::move(m_result) ]()
auto operation = [ clip = m_binClip, image = std::move(m_result), this ]()
{
clip->setThumbnail(image);
if (m_reloadAll) {
clip->updateTimelineClips({TimelineModel::ReloadThumbRole});
}
return true;
};
auto reverse = [ clip = m_binClip, image = std::move(old) ]()
auto reverse = [ clip = m_binClip, image = std::move(old), this ]()
{
clip->setThumbnail(image);
if (m_reloadAll) {
clip->updateTimelineClips({TimelineModel::ReloadThumbRole});
}
return true;
};
ok = operation();
......@@ -155,8 +170,5 @@ bool ThumbJob::commitResult(Fun &undo, Fun &redo)
UPDATE_UNDO_REDO_NOLOCK(operation, reverse, undo, redo);
}
}
if (!m_inCache) {
ThumbnailCache::get()->storeThumbnail(m_binClip->clipId(), m_frameNumber, m_result, m_persistent);
}
return ok;
}
......@@ -42,7 +42,7 @@ public:
@param frameNumber is the frame to extract. Leave to -1 for default
@param persistent: if true, we will use the persistent cache (for query and saving)
*/
ThumbJob(const QString &binId, int imageHeight, int frameNumber = -1, bool persistent = false);
ThumbJob(const QString &binId, int imageHeight, int frameNumber = -1, bool persistent = false, bool reloadAllThumbs = false);
const QString getDescription() const override;
......@@ -63,6 +63,7 @@ private:
QImage m_result;
bool m_done{false};
bool m_persistent;
bool m_reloadAll;
bool m_inCache{false};
bool m_subClip{false}; // true if we operate on a subclip
};
......@@ -40,6 +40,7 @@ ClipModel::ClipModel(std::shared_ptr<TimelineModel> parent, std::shared_ptr<Mlt:
, m_producer(std::move(prod))
, m_effectStack(EffectStackModel::construct(m_producer, {ObjectType::TimelineClip, m_id}, parent->m_undoStack))
, m_binClipId(binClipId)
, forceThumbReload(false)
{
m_producer->set("kdenlive:id", binClipId.toUtf8().constData());
m_producer->set("_kdenlive_cid", m_id);
......
......@@ -151,6 +151,8 @@ protected:
QString m_binClipId; // This is the Id of the bin clip this clip corresponds to.
bool m_endlessResize; // Whether this clip can be freely resized
bool forceThumbReload; // Used to trigger a forced thumb reload, when producer changes
};
#endif
......@@ -198,7 +198,7 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles[ItemIdRole] = "item";
roles[ItemATrack] = "a_track";
roles[HasAudio] = "hasAudio";
roles[ReloadThumb] = "reloadThumb";
roles[ReloadThumbRole] = "reloadThumb";
return roles;
}
......@@ -219,8 +219,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
}
return id;
}
// qDebug() << "REQUESTING DATA "<<roleNames()[role]<<index;
if (isClip(id)) {
//qDebug() << "REQUESTING DATA "<<roleNames()[role]<<index;
std::shared_ptr<ClipModel> clip = m_allClips.at(id);
// Get data for a clip
switch (role) {
......@@ -284,8 +284,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
return clip->fadeIn();
case FadeOutRole:
return clip->fadeOut();
case ReloadThumb:
return false;
case ReloadThumbRole:
return clip->forceThumbReload;
default:
break;
}
......@@ -458,7 +458,7 @@ void TimelineItemModel::notifyChange(const QModelIndex &topleft, const QModelInd
emit dataChanged(topleft, bottomright, roles);
}
void TimelineItemModel::notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, QVector<int> roles)
void TimelineItemModel::notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, const QVector<int> &roles)
{
emit dataChanged(topleft, bottomright, roles);
}
......
......@@ -82,7 +82,7 @@ public:
Q_INVOKABLE void setTrackProperty(int tid, const QString &name, const QString &value);
Q_INVOKABLE QVariant getTrackProperty(int tid, const QString &name);
void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, bool start, bool duration, bool updateThumb) override;
void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, QVector<int> roles) override;
void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, const QVector<int> &roles) override;
void buildTrackCompositing();
const QString groupsData();
bool loadGroups(const QString &groupsData);
......
......@@ -2016,9 +2016,12 @@ void TimelineModel::replugClip(int clipId)
}
}
void TimelineModel::requestClipUpdate(int clipId, QVector<int> roles)
void TimelineModel::requestClipUpdate(int clipId, const QVector<int> &roles)
{
QModelIndex modelIndex = makeClipIndexFromID(clipId);
if (roles.contains(TimelineModel::ReloadThumbRole)) {
m_allClips[clipId]->forceThumbReload = !m_allClips[clipId]->forceThumbReload;
}
notifyChange(modelIndex, modelIndex, roles);
}
......
......@@ -133,7 +133,7 @@ public:
IsCompositionRole, /// clip only
FileHashRole, /// clip only
SpeedRole, /// clip only
ReloadThumb, /// clip only
ReloadThumbRole, /// clip only
ItemATrack, /// composition only
ItemIdRole
};
......@@ -489,7 +489,7 @@ public:
virtual void adjustAssetRange(int clipId, int in, int out);
void requestClipReload(int clipId);
void requestClipUpdate(int clipId, QVector<int> roles);
void requestClipUpdate(int clipId, const QVector<int> &roles);
/** @brief Returns the effectstack of a given clip. */
std::shared_ptr<EffectStackModel> getClipEffectStack(int itemId);
......@@ -634,7 +634,7 @@ protected:
virtual void _endRemoveRows() = 0;
virtual void _endInsertRows() = 0;
virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, bool start, bool duration, bool updateThumb) = 0;
virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, QVector<int>) = 0;
virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, const QVector<int> &roles) = 0;
virtual QModelIndex makeClipIndexFromID(int) const = 0;
virtual QModelIndex makeCompositionIndexFromID(int) const = 0;
virtual QModelIndex makeTrackIndexFromID(int) const = 0;
......
......@@ -63,7 +63,9 @@ Rectangle {
property string hash: 'ccc' //TODO
property double speed: 1.0
property color borderColor: 'black'
property bool reloadThumb: false
property bool forceReloadThumb: false
property alias inSource: inThumbnail.source
property alias outSource: outThumbnail.source
width : clipDuration * timeScale;
signal clicked(var clip, int shiftClick)
......@@ -128,10 +130,12 @@ Rectangle {
x = modelStart * timeScale;
}
onReloadThumbChanged: {
if (mltService === 'color') {
var newColor = getColor()
color = selected ? newColor : Qt.darker(newColor)
onForceReloadThumbChanged: {
if (inThumbnail.visible) {
clipRoot.inSource = ''
clipRoot.inSource = inThumbPath
clipRoot.outSource = ''
clipRoot.outSource = outThumbPath
}
}
......@@ -336,15 +340,16 @@ Rectangle {
clip: true
Image {
id: outThumbnail
visible: timeline.showThumbnails && mltService != 'color' && !isAudio && clipStatus < 2
opacity: parentTrack.isAudio || parentTrack.isHidden ? 0.2 : 1
visible: inThumbnail.visible
opacity: inThumbnail.opacity
anchors.top: container.top
anchors.right: container.right
anchors.bottom: container.bottom
anchors.rightMargin: Math.min(0, container.width - 2 * inThumbnail.width)
width: height * 16.0/9.0
width: inThumbnail.width
fillMode: Image.PreserveAspectFit
asynchronous: true
cache: false
source: outThumbPath
}
......@@ -358,6 +363,7 @@ Rectangle {
width: height * 16.0/9.0
fillMode: Image.PreserveAspectFit
asynchronous: true
cache: false
source: inThumbPath
}
......
......@@ -102,12 +102,6 @@ Column{
value: model.groupDrag
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "reloadThumb"
value: model.reloadThumb
when: loader.status == Loader.Ready && loader.item.isComposition === false
}
Binding {
target: loader.item
property: "clipStatus"
......@@ -186,13 +180,12 @@ Column{
value: model.resource
when: loader.status == Loader.Ready && !loader.item.isComposition
}
/*Binding {
Binding {
target: loader.item
property: "clipId"
value: model.item
when: loader.status == Loader.Ready
property: "forceReloadThumb"
value: model.reloadThumb
when: loader.status == Loader.Ready && !loader.item.isComposition
}
*/
Binding {
target: loader.item
property: "binId"
......
......@@ -663,7 +663,7 @@ Rectangle {
flickableItem.interactive: false
clip: true
Rectangle {
width: Math.max(scrollView.width - scrollView.__verticalScrollBar.width, timeline.duration * timeScale + root.projectMargin)
width: Math.max(root.width - headerWidth - scrollView.__verticalScrollBar.width, timeline.duration * timeScale + root.projectMargin)
height: trackHeaders.height
color: activePalette.window
id: tracksContainerArea
......
......@@ -20,6 +20,7 @@
#include "bin/projectclip.h"
#include "bin/projectitemmodel.h"
#include "core.h"
#include "utils/thumbnailcache.hpp"
#include <QCryptographicHash>
#include <QDebug>
......@@ -31,10 +32,6 @@ ThumbnailProvider::ThumbnailProvider()
: QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading)
, m_profile(pCore->getCurrentProfilePath().toUtf8().constData())
{
KImageCache::deleteCache(QStringLiteral("kdenlive-timeline-thumbs"));
m_cache = new KImageCache(QStringLiteral("kdenlive-timeline-thumbs"), 10000000);
m_cache->clear();
m_cache->setEvictionPolicy(KSharedDataCache::EvictOldest);
int width = 180 * m_profile.dar();
width += width % 8;
m_profile.set_height(180);
......@@ -44,13 +41,11 @@ ThumbnailProvider::ThumbnailProvider()
ThumbnailProvider::~ThumbnailProvider()
{
delete m_cache;
}
void ThumbnailProvider::resetProject()
{
m_producers.clear();
m_cache->clear();
}
QImage ThumbnailProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
......@@ -59,57 +54,64 @@ QImage ThumbnailProvider::requestImage(const QString &id, QSize *size, const QSi
// id is binID/mlt_service/resource#frameNumber
int index = id.lastIndexOf('#');
if (index != -1) {
if (true /*index != -1*/) {
QString binId = id.section('/', 0, 0);
QString service = id.section('/', 1, 1);
QString resource = id.section('/', 2);
int frameNumber = id.mid(index + 1).toInt();
resource = resource.left(resource.lastIndexOf('#'));
// properties.set("_profile", m_profile.get_profile(), 0);
// QString key = cacheKey(properties, service, resource, hash, frameNumber);
// result = getCachedThumbnail(key);
if (ThumbnailCache::get()->hasThumbnail(binId, frameNumber, false)) {
result = ThumbnailCache::get()->getThumbnail(binId, frameNumber);
*size = result.size();
return result;
}
const QString key = binId + "#" + QString::number(frameNumber);
if (!m_cache->findImage(key, &result)) {
if (service == "avformat-novalidate")
service = "avformat";
else if (service.startsWith("xml"))
service = "xml-nogl";
Mlt::Producer *producer = nullptr;
if (m_producers.contains(binId.toInt())) {
producer = m_producers.object(binId.toInt());
if (service == "avformat-novalidate")
service = "avformat";
else if (service.startsWith("xml"))
service = "xml-nogl";
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(binId);
if (binClip) {
std::shared_ptr<Mlt::Producer> prod = binClip->thumbProducer();
result = makeThumbnail(prod, frameNumber, requestedSize);
ThumbnailCache::get()->storeThumbnail(binId, frameNumber, result, false);
}
/*if (m_producers.contains(binId.toInt())) {
producer = m_producers.object(binId.toInt());
} else {
m_binClip->thumbProducer();
if (!resource.isEmpty()) {
producer = new Mlt::Producer(m_profile, service.toUtf8().constData(), resource.toUtf8().constData());
} else {
if (!resource.isEmpty()) {
producer = new Mlt::Producer(m_profile, service.toUtf8().constData(), resource.toUtf8().constData());
} else {
producer = new Mlt::Producer(m_profile, service.toUtf8().constData());
}
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(binId);
if (binClip) {
std::shared_ptr<Mlt::Producer> projectProducer = binClip->originalProducer();
Mlt::Properties original(projectProducer->get_properties());
Mlt::Properties cloneProps(producer->get_properties());
cloneProps.pass_list(original, "video_index,force_aspect_num,force_aspect_den,force_aspect_ratio,force_fps,force_progressive,force_tff,"
"force_colorspace,set.force_full_luma,templatetext,autorotate,xmldata");
}
Mlt::Filter scaler(m_profile, "swscale");
Mlt::Filter padder(m_profile, "resize");
Mlt::Filter converter(m_profile, "avcolor_space");
producer->attach(scaler);
producer->attach(padder);
producer->attach(converter);
m_producers.insert(binId.toInt(), producer);
producer = new Mlt::Producer(m_profile, service.toUtf8().constData());
}
if ((producer != nullptr) && producer->is_valid()) {
// result = KThumb::getFrame(producer, frameNumber, 0, 0);
result = makeThumbnail(producer, frameNumber, requestedSize);
m_cache->insertImage(key, result);
} else {
qDebug() << "INVALID PRODUCER; " << service << " / " << resource;
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(binId);
if (binClip) {
std::shared_ptr<Mlt::Producer> projectProducer = binClip->originalProducer();
Mlt::Properties original(projectProducer->get_properties());
Mlt::Properties cloneProps(producer->get_properties());
cloneProps.pass_list(original, "video_index,force_aspect_num,force_aspect_den,force_aspect_ratio,force_fps,force_progressive,force_tff,"
"force_colorspace,set.force_full_luma,templatetext,autorotate,xmldata");
}
Mlt::Filter scaler(m_profile, "swscale");
Mlt::Filter padder(m_profile, "resize");
Mlt::Filter converter(m_profile, "avcolor_space");
producer->attach(scaler);
producer->attach(padder);
producer->attach(converter);
m_producers.insert(binId.toInt(), producer);
}
if (size) *size = result.size();
if ((producer != nullptr) && producer->is_valid()) {
// result = KThumb::getFrame(producer, frameNumber, 0, 0);
result = makeThumbnail(producer, frameNumber, requestedSize);
ThumbnailCache::get()->storeThumbnail(binId, frameNumber, result, false);
//m_cache->insertImage(key, result);
} else {
qDebug() << "INVALID PRODUCER; " << service << " / " << resource;
}*/
}
if (size) *size = result.size();
return result;
}
......@@ -131,7 +133,7 @@ QString ThumbnailProvider::cacheKey(Mlt::Properties &properties, const QString &
return key;
}
QImage ThumbnailProvider::makeThumbnail(Mlt::Producer *producer, int frameNumber, const QSize &requestedSize)
QImage ThumbnailProvider::makeThumbnail(std::shared_ptr<Mlt::Producer>producer, int frameNumber, const QSize &requestedSize)
{
Q_UNUSED(requestedSize)
......
......@@ -24,6 +24,7 @@
#include <QQuickImageProvider>
#include <mlt++/MltProducer.h>
#include <mlt++/MltProfile.h>
#include <memory>
class ThumbnailProvider : public QQuickImageProvider
{
......@@ -35,9 +36,8 @@ public:
private:
QString cacheKey(Mlt::Properties &properties, const QString &service, const QString &resource, const QString &hash, int frameNumber);
QImage makeThumbnail(Mlt::Producer *, int frameNumber, const QSize &requestedSize);
QImage makeThumbnail(std::shared_ptr<Mlt::Producer> producer, int frameNumber, const QSize &requestedSize);
Mlt::Profile m_profile;
KImageCache *m_cache;
QCache<int, Mlt::Producer> m_producers;
};
......
......@@ -136,12 +136,22 @@ QImage ThumbnailCache::getThumbnail(const QString &binId, int pos, bool volatile
void ThumbnailCache::storeThumbnail(const QString &binId, int pos, const QImage &img, bool persistent)
{
QMutexLocker locker(&m_mutex);
auto key = getKey(binId, pos);
const QString key = getKey(binId, pos);
if (persistent) {
bool ok = false;
QDir thumbFolder = getDir(&ok);
if (ok) {
img.save(thumbFolder.absoluteFilePath(key));
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, img.byteCount());
}
} else {
m_volatileCache->insert(key, img, img.byteCount());
......@@ -152,14 +162,23 @@ void ThumbnailCache::storeThumbnail(const QString &binId, int pos, const QImage
void ThumbnailCache::invalidateThumbsForClip(const QString &binId)
{
QMutexLocker locker(&m_mutex);
for (int pos : m_storedVolatile.at(binId)) {
auto key = getKey(binId, pos);
m_volatileCache->remove(key);
if (m_storedVolatile.find(binId) != m_storedVolatile.end()) {
for (int pos : m_storedVolatile.at(binId)) {
auto key = getKey(binId, pos);
m_volatileCache->remove(key);
}
m_storedVolatile.erase(binId);
}
bool ok = false;
QDir thumbFolder = getDir(&ok);
if (ok && m_storedOnDisk.find(binId) != m_storedOnDisk.end()) {
// Remove persistent cache
for (int pos : m_storedOnDisk.at(binId)) {
auto key = getKey(binId, pos);
QFile::remove(thumbFolder.absoluteFilePath(key));
}
m_storedOnDisk.erase(binId);
}
m_storedVolatile.erase(binId);
Q_ASSERT(false);
// TODO implement the invalidation for persistent cache
}
// static
......
......@@ -90,4 +90,5 @@ protected:
// 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<QString, std::vector<int>> m_storedVolatile;
std::unordered_map<QString, std::vector<int>> m_storedOnDisk;
};
......@@ -45,7 +45,7 @@ using namespace fakeit;
Spy(Method(mock, _endInsertRows)); \
Spy(Method(mock, _endRemoveRows)); \
Spy(OverloadedMethod(mock, notifyChange, void(const QModelIndex &, const QModelIndex &, bool, bool, bool))); \
Spy(OverloadedMethod(mock, notifyChange, void(const QModelIndex &, const QModelIndex &, QVector<int>)));
Spy(OverloadedMethod(mock, notifyChange, void(const QModelIndex &, const QModelIndex &, const QVector<int> &)));
#define NO_OTHERS() \
VerifyNoOtherInvocations(Method(timMock, _beginRemoveRows)); \
......@@ -53,7 +53,7 @@ using namespace fakeit;
VerifyNoOtherInvocations(Method(timMock, _endRemoveRows)); \
VerifyNoOtherInvocations(Method(timMock, _endInsertRows)); \
VerifyNoOtherInvocations(OverloadedMethod(timMock, notifyChange, void(const QModelIndex &, const QModelIndex &, bool, bool, bool))); \
VerifyNoOtherInvocations(OverloadedMethod(timMock, notifyChange, void(const QModelIndex &, const QModelIndex &, QVector<int>))); \
VerifyNoOtherInvocations(OverloadedMethod(timMock, notifyChange, void(const QModelIndex &, const QModelIndex &, const QVector<int> &))); \
RESET(timMock);
#define CHECK_MOVE(times) \
......
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