File indexing completed on 2024-04-14 04:47:55

0001 /*
0002     SPDX-FileCopyrightText: 2006-2008 Marco Gulino <marco@kmobiletools.org>
0003     SPDX-FileCopyrightText: Jean-Baptiste Mardelle <jb@kdenlive.org>
0004 
0005     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "mltpreview.h"
0009 #include "../src/lib/localeHandling.h"
0010 
0011 #include <QDebug>
0012 #include <QImage>
0013 #include <QVarLengthArray>
0014 #include <QtGlobal>
0015 
0016 #include <KPluginFactory>
0017 
0018 K_PLUGIN_CLASS_WITH_JSON(MltPreview, "mltpreview.json")
0019 
0020 MltPreview::MltPreview(QObject *parent, const QVariantList &args)
0021     : KIO::ThumbnailCreator(parent, args)
0022 {
0023     // After initialising the MLT factory, set the locale back from user default to C
0024     // to ensure numbers are always serialised with . as decimal point.
0025     Mlt::Factory::init();
0026     LocaleHandling::resetLocale();
0027 }
0028 
0029 MltPreview::~MltPreview()
0030 {
0031     Mlt::Factory::close();
0032 }
0033 
0034 KIO::ThumbnailResult MltPreview::create(const KIO::ThumbnailRequest &request)
0035 {
0036     int width = request.targetSize().width();
0037     int height = request.targetSize().height();
0038     std::unique_ptr<Mlt::Profile> profile(new Mlt::Profile());
0039     std::shared_ptr<Mlt::Producer> producer(new Mlt::Producer(*profile.get(), request.url().toLocalFile().toUtf8().data()));
0040 
0041     if (producer == nullptr || !producer->is_valid() || producer->is_blank()) {
0042         return KIO::ThumbnailResult::fail();
0043     }
0044 
0045     uint variance = 10;
0046     int ct = 1;
0047     double ar = profile->dar();
0048     if (ar < 1e-6) {
0049         ar = 1.0;
0050     }
0051     int wanted_width = width;
0052     int wanted_height = int(width / ar);
0053     if (wanted_height > height) {
0054         wanted_height = height;
0055         wanted_width = int(height * ar);
0056     }
0057     // We don't need audio
0058     producer->set("audio_index", -1);
0059 
0060     // Add normalizers    
0061     Mlt::Filter scaler(*profile.get(), "swscale");
0062     Mlt::Filter padder(*profile.get(), "resize");
0063     Mlt::Filter converter(*profile.get(), "avcolor_space");
0064 
0065     if (scaler.is_valid()) {
0066         producer->attach(scaler);
0067     }
0068     if (padder.is_valid()) {
0069         producer->attach(padder);
0070     }
0071     if (converter.is_valid()) {
0072         producer->attach(converter);
0073     }
0074 
0075     QImage img;
0076     int length = producer->get_length();
0077     if (length < 1) {
0078         return KIO::ThumbnailResult::fail();
0079     }
0080     int frame = qMin(75, length - 1);
0081     while (variance <= 40 && ct < 4 && frame < length) {
0082         img = getFrame(producer, frame, wanted_width, wanted_height);
0083         variance = uint(imageVariance(img));
0084         frame += 100 * ct;
0085         ct++;
0086     }
0087 
0088     if (img.isNull()) {
0089         return KIO::ThumbnailResult::fail();
0090     }
0091 
0092     return KIO::ThumbnailResult::pass(img);
0093 }
0094 
0095 QImage MltPreview::getFrame(std::shared_ptr<Mlt::Producer> producer, int framepos, int width, int height)
0096 {
0097     QImage mltImage(width, height, QImage::Format_ARGB32);
0098     if (producer == nullptr) {
0099         return mltImage;
0100     }
0101     producer->seek(framepos);
0102     std::shared_ptr<Mlt::Frame> frame(producer->get_frame());
0103     if (frame == nullptr || !frame->is_valid()) {
0104         return mltImage;
0105     }
0106 
0107     mlt_image_format format = mlt_image_rgba;
0108     const uchar *imagedata = frame->get_image(format, width, height);
0109     if (imagedata != nullptr) {
0110         memcpy(mltImage.bits(), imagedata, size_t(width * height * 4));
0111         mltImage = mltImage.rgbSwapped();
0112     }
0113     return mltImage;
0114 }
0115 
0116 int MltPreview::imageVariance(const QImage &image)
0117 {
0118     if (image.isNull()) {
0119         return 0;
0120     }
0121     int delta = 0;
0122     int avg = 0;
0123     int bytes = int(image.sizeInBytes());
0124     int STEPS = bytes / 2;
0125     if (STEPS < 1) {
0126         return 0;
0127     }
0128     QVarLengthArray<uchar> pivot(STEPS);
0129     qDebug() << "Using " << STEPS << " steps\n";
0130     const uchar *bits = image.bits();
0131     // First pass: get pivots and taking average
0132     for (int i = 0; i < STEPS; i++) {
0133         pivot[i] = bits[2 * i];
0134         avg += pivot.at(i);
0135     }
0136     avg = avg / STEPS;
0137     // Second Step: calculate delta (average?)
0138     for (int i = 0; i < STEPS; ++i) {
0139         int curdelta = abs(avg - pivot.at(i));
0140         delta += curdelta;
0141     }
0142     return delta / STEPS;
0143 }
0144 
0145 #include "mltpreview.moc"