File indexing completed on 2024-12-01 04:28:35
0001 /* 0002 SPDX-FileCopyrightText: 2021 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 This file is part of Kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "cachetask.h" 0009 #include "bin/projectclip.h" 0010 #include "bin/projectitemmodel.h" 0011 #include "core.h" 0012 #include "doc/kthumb.h" 0013 #include "kdenlivesettings.h" 0014 #include "utils/thumbnailcache.hpp" 0015 0016 #include "xml/xml.hpp" 0017 #include <KLocalizedString> 0018 #include <QFile> 0019 #include <QImage> 0020 #include <QString> 0021 #include <QtMath> 0022 #include <set> 0023 0024 CacheTask::CacheTask(const ObjectId &owner, int thumbsCount, int in, int out, QObject *object) 0025 : AbstractTask(owner, AbstractTask::CACHEJOB, object) 0026 , m_fullWidth(qFuzzyCompare(pCore->getCurrentSar(), 1.0) ? 0 : qRound(pCore->thumbProfile().height() * pCore->getCurrentDar())) 0027 , m_thumbsCount(thumbsCount) 0028 , m_in(in) 0029 , m_out(out) 0030 { 0031 m_description = i18n("Video thumbs"); 0032 if (m_fullWidth % 2 > 0) { 0033 m_fullWidth++; 0034 } 0035 } 0036 0037 CacheTask::~CacheTask() {} 0038 0039 void CacheTask::start(const ObjectId &owner, int thumbsCount, int in, int out, QObject *object, bool force) 0040 { 0041 if (pCore->taskManager.hasPendingJob(owner, AbstractTask::CACHEJOB)) { 0042 return; 0043 } 0044 CacheTask *task = new CacheTask(owner, thumbsCount, in, out, object); 0045 // Otherwise, start a new audio levels generation thread. 0046 task->m_isForce = force; 0047 pCore->taskManager.startTask(owner.itemId, task); 0048 } 0049 0050 void CacheTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip) 0051 { 0052 // Fetch thumbnail 0053 if (binClip->clipType() != ClipType::Audio) { 0054 std::unique_ptr<Mlt::Producer> thumbProd(nullptr); 0055 int duration = m_out > 0 ? m_out - m_in : binClip->getFramePlaytime(); 0056 std::set<int> frames; 0057 int steps = qCeil(qMax(pCore->getCurrentFps(), double(duration) / m_thumbsCount)); 0058 int pos = m_in; 0059 for (int i = 1; i <= m_thumbsCount && pos <= m_in + duration; ++i) { 0060 frames.insert(pos); 0061 pos = m_in + (steps * i); 0062 } 0063 int size = int(frames.size()); 0064 int count = 0; 0065 const QString clipId = QString::number(m_owner.itemId); 0066 for (int i : frames) { 0067 m_progress = 100 * count / size; 0068 QMetaObject::invokeMethod(m_object, "updateJobProgress"); 0069 count++; 0070 if (m_isCanceled || pCore->taskManager.isBlocked()) { 0071 break; 0072 } 0073 if (ThumbnailCache::get()->hasThumbnail(clipId, i)) { 0074 continue; 0075 } 0076 if (thumbProd == nullptr) { 0077 thumbProd = binClip->getThumbProducer(); 0078 } 0079 if (thumbProd == nullptr) { 0080 // Thumb producer not available 0081 break; 0082 } 0083 thumbProd->seek(i); 0084 QScopedPointer<Mlt::Frame> frame(thumbProd->get_frame()); 0085 if (frame != nullptr && frame->is_valid()) { 0086 frame->set("consumer.deinterlacer", "onefield"); 0087 frame->set("consumer.top_field_first", -1); 0088 frame->set("consumer.rescale", "nearest"); 0089 QImage result = KThumb::getFrame(frame.data(), 0, 0, m_fullWidth); 0090 if (!result.isNull() && !m_isCanceled) { 0091 qDebug() << "==== CACHING FRAME: " << i; 0092 ThumbnailCache::get()->storeThumbnail(clipId, i, result, true); 0093 } 0094 } 0095 } 0096 } 0097 } 0098 0099 void CacheTask::run() 0100 { 0101 AbstractTaskDone whenFinished(m_owner.itemId, this); 0102 if (m_isCanceled || pCore->taskManager.isBlocked()) { 0103 return; 0104 } 0105 QMutexLocker lock(&m_runMutex); 0106 auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.itemId)); 0107 if (binClip) { 0108 generateThumbnail(binClip); 0109 } 0110 return; 0111 }