File: mltpreview.cpp

package info (click to toggle)
kdenlive 25.12.1-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 125,932 kB
  • sloc: cpp: 206,733; xml: 11,858; python: 1,139; ansic: 1,054; javascript: 578; sh: 389; makefile: 15
file content (148 lines) | stat: -rw-r--r-- 4,225 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
    SPDX-FileCopyrightText: 2006-2008 Marco Gulino <marco@kmobiletools.org>
    SPDX-FileCopyrightText: Jean-Baptiste Mardelle <jb@kdenlive.org>

    SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/

#include "mltpreview.h"
#include "../src/lib/localeHandling.h"

#include <QDebug>
#include <QImage>
#include <QVarLengthArray>
#include <QtGlobal>

#include <KPluginFactory>

K_PLUGIN_CLASS_WITH_JSON(MltPreview, "mltpreview.json")

MltPreview::MltPreview(QObject *parent, const QVariantList &args)
    : KIO::ThumbnailCreator(parent, args)
{
    // block MLT Qt5 modules to prevent crashes
    qputenv("MLT_REPOSITORY_DENY", "libmltqt:libmltglaxnimate");

    // After initialising the MLT factory, set the locale back from user default to C
    // to ensure numbers are always serialised with . as decimal point.
    Mlt::Factory::init();
    LocaleHandling::resetLocale();
}

MltPreview::~MltPreview()
{
    Mlt::Factory::close();
}

KIO::ThumbnailResult MltPreview::create(const KIO::ThumbnailRequest &request)
{
    int width = request.targetSize().width();
    int height = request.targetSize().height();
    std::unique_ptr<Mlt::Profile> profile(new Mlt::Profile());
    std::shared_ptr<Mlt::Producer> producer(new Mlt::Producer(*profile.get(), "xml-nogl", request.url().toLocalFile().toUtf8().data()));

    if (producer == nullptr || !producer->is_valid() || producer->is_blank()) {
        return KIO::ThumbnailResult::fail();
    }

    uint variance = 10;
    int ct = 1;
    double ar = profile->dar();
    if (ar < 1e-6) {
        ar = 1.0;
    }
    int wanted_width = width;
    int wanted_height = int(width / ar);
    if (wanted_height > height) {
        wanted_height = height;
        wanted_width = int(height * ar);
    }
    // We don't need audio
    producer->set("audio_index", -1);

    // Add normalizers    
    Mlt::Filter scaler(*profile.get(), "swscale");
    Mlt::Filter padder(*profile.get(), "resize");
    Mlt::Filter converter(*profile.get(), "avcolor_space");

    if (scaler.is_valid()) {
        producer->attach(scaler);
    }
    if (padder.is_valid()) {
        producer->attach(padder);
    }
    if (converter.is_valid()) {
        producer->attach(converter);
    }

    QImage img;
    int length = producer->get_length();
    if (length < 1) {
        return KIO::ThumbnailResult::fail();
    }
    int frame = qMin(75, length - 1);
    while (variance <= 40 && ct < 4 && frame < length) {
        img = getFrame(producer, frame, wanted_width, wanted_height);
        variance = uint(imageVariance(img));
        frame += 100 * ct;
        ct++;
    }

    if (img.isNull()) {
        return KIO::ThumbnailResult::fail();
    }

    return KIO::ThumbnailResult::pass(img);
}

QImage MltPreview::getFrame(std::shared_ptr<Mlt::Producer> producer, int framepos, int width, int height)
{
    QImage mltImage(width, height, QImage::Format_ARGB32);
    if (producer == nullptr) {
        return mltImage;
    }
    producer->seek(framepos);
    std::shared_ptr<Mlt::Frame> frame(producer->get_frame());
    if (frame == nullptr || !frame->is_valid()) {
        return mltImage;
    }

    mlt_image_format format = mlt_image_rgba;
    const uchar *imagedata = frame->get_image(format, width, height);
    if (imagedata != nullptr) {
        memcpy(mltImage.bits(), imagedata, size_t(width * height * 4));
        mltImage = mltImage.rgbSwapped();
    }
    return mltImage;
}

int MltPreview::imageVariance(const QImage &image)
{
    if (image.isNull()) {
        return 0;
    }
    int delta = 0;
    int avg = 0;
    int bytes = int(image.sizeInBytes());
    int STEPS = bytes / 2;
    if (STEPS < 1) {
        return 0;
    }
    QVarLengthArray<uchar> pivot(STEPS);
    // qDebug() << "Using " << STEPS << " steps\n";
    const uchar *bits = image.bits();
    // First pass: get pivots and taking average
    for (int i = 0; i < STEPS; i++) {
        pivot[i] = bits[2 * i];
        avg += pivot.at(i);
    }
    avg = avg / STEPS;
    // Second Step: calculate delta (average?)
    for (int i = 0; i < STEPS; ++i) {
        int curdelta = abs(avg - pivot.at(i));
        delta += curdelta;
    }
    return delta / STEPS;
}

#include "mltpreview.moc"