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 }