]> git.sesse.net Git - kdenlive/blob - thumbnailer/westleypreview.cpp
Update to the new MLT structure (based on patch by Dan Dennedy)
[kdenlive] / thumbnailer / westleypreview.cpp
1 /***************************************************************************
2    Copyright (C) 2006-2008
3    by Marco Gulino <marco@kmobiletools.org>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the
17    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18     Boston, MA 02110-1301, USA.
19  ***************************************************************************/
20
21 #include "westleypreview.h"
22
23 #include <qfile.h>
24 #include <qimage.h>
25 #include <QtCore/QVarLengthArray>
26
27 #include <kstandarddirs.h>
28 #include <krandomsequence.h>
29 #include <qdatetime.h>
30 #include <QProcess>
31 #include <kdebug.h>
32 #include <ktempdir.h>
33 #include <kurl.h>
34 #include <qfileinfo.h>
35 #include <KTemporaryFile>
36
37 #include <unistd.h>
38
39
40 #define DBG_AREA
41
42 //#include "config.h"
43 extern "C"
44 {
45     KDE_EXPORT ThumbCreator *new_creator() {
46         return new MltPreview;
47     }
48 }
49
50 MltPreview::MltPreview() :
51         QObject(),
52         ThumbCreator(),
53         m_meltProcess(0),
54         m_rand(0)
55 {
56 }
57
58 MltPreview::~MltPreview()
59 {
60     delete m_rand;
61     delete m_meltProcess;
62 }
63
64 bool MltPreview::startAndWaitProcess(const QStringList &args)
65 {
66     kDebug(DBG_AREA) << "MltPreview: starting process with args: " << args << endl;
67     m_meltProcess->start(args.join(" "));
68     if (! m_meltProcess->waitForStarted()) {
69         kDebug(DBG_AREA) << "MltPreview: PROCESS NOT STARTED!!! exiting\n";
70         return false;
71     }
72     if (! m_meltProcess->waitForFinished()) {
73         kDebug(DBG_AREA) << "MltPreview: PROCESS DIDN'T FINISH!! exiting\n";
74         m_meltProcess->close();
75         return false;
76     }
77     kDebug() << "MltPreview: process started and ended correctly\n";
78     return true;
79 }
80
81 bool MltPreview::create(const QString &path, int width, int /*height*/, QImage &img)
82 {
83     QFileInfo fi(path);
84     m_playerBin = KStandardDirs::findExe("melt");
85     if (m_playerBin.isEmpty()) {
86         kDebug(DBG_AREA) << "MltPreview: melt not found, exiting.\n";
87         return false;
88     }
89
90     fileinfo.seconds = 0;
91     fileinfo.fps = 0;
92
93     m_rand = new KRandomSequence(QDateTime::currentDateTime().toTime_t());
94     m_meltProcess = new QProcess();
95     KUrl furl(path);
96     kDebug(DBG_AREA) << "videopreview: url=" << furl << "; local:" << furl.isLocalFile() << endl;
97     fileinfo.towidth = width;
98     fileinfo.toheight = width * 3 / 4;
99     QImage pix;
100 //    if(furl.isLocalFile())
101 //    {
102     QStringList args;
103     //TODO: modify melt so that it can return some infos about a MLT XML clip (duration, track number,fps,...)
104     // without actually playing the file
105     /*
106         args << playerBin << QString("\"" + path + "\"") << "-file-info";
107
108         kDebug(DBG_AREA) << "videopreview: starting process: --_" << " " << args.join(" ") << "_--\n";
109         if (! startAndWaitProcess(args) ) return NULL;
110
111         QString information=QString(meltProcess->readAllStandardOutput() );
112         QRegExp findInfos("ID_VIDEO_FPS=([\\d]*).*ID_LENGTH=([\\d]*).*");
113         if(findInfos.indexIn( information) == -1 )
114         {
115             kDebug(DBG_AREA) << "videopreview: No information found, exiting\n";
116             return NULL;
117         }
118         fileinfo.seconds =findInfos.cap(2).toInt();
119         fileinfo.fps=findInfos.cap(1).toInt();
120         */
121     fileinfo.seconds = 250;
122     fileinfo.fps = 25;
123
124     const int LASTTRY = 3;
125     for (int i = 0; i <= LASTTRY; i++) {
126         pix = getFrame(path);
127         if (!pix.isNull()) {
128             uint variance = imageVariance(pix);
129             kDebug(DBG_AREA) << "videopreview: " << QFileInfo(path).fileName() << " frame variance: " << variance << "; " <<
130             ((variance <= 40 && (i != LASTTRY - 1)) ? "!!!DROPPING!!!" : "GOOD :-)") << endl;
131             if (variance > 40 || i == LASTTRY - 1) break;
132         }
133     }
134     if (pix.isNull()) {
135         return false;
136     }
137
138     if (pix.depth() != 32)
139         img = pix.convertToFormat(QImage::Format_RGB32);
140     else img = pix;
141     return true;
142 }
143
144 QImage MltPreview::getFrame(const QString &path)
145 {
146     QStringList args;
147     const int START = 25;
148     const int RANGE = 500;
149     args.clear();
150     args << m_playerBin << "\"" + path + "\"";
151
152     unsigned long start = (unsigned long)(START + (m_rand->getDouble() * RANGE));
153     args << QString("in=%1").arg(start) << QString("out=%1").arg(start) << "-consumer";
154
155     KTemporaryFile temp;
156     temp.setSuffix(".png");
157     temp.open();
158     args << QString("avformat:%1").arg(temp.fileName()) << "vframes=1" << "f=rawvideo" << "vcodec=png" << QString("s=%1x%2").arg(fileinfo.towidth).arg(fileinfo.toheight);
159     if (! startAndWaitProcess(args)) return QImage();
160     QImage retpix(temp.fileName());
161     temp.close();
162     return retpix;
163 }
164
165
166 uint MltPreview::imageVariance(QImage image)
167 {
168     uint delta = 0;
169     uint avg = 0;
170     uint bytes = image.numBytes();
171     uint STEPS = bytes / 2;
172     QVarLengthArray<uchar> pivot(STEPS);
173     kDebug(DBG_AREA) << "Using " << STEPS << " steps\n";
174     uchar *bits = image.bits();
175     // First pass: get pivots and taking average
176     for (uint i = 0; i < STEPS ; i++) {
177         pivot[i] = bits[i*(bytes/STEPS)];
178         avg += pivot[i];
179     }
180     avg = avg / STEPS;
181     // Second Step: calculate delta (average?)
182     for (uint i = 0; i < STEPS; i++) {
183         int curdelta = abs(int(avg - pivot[i]));
184         delta += curdelta;
185     }
186     return delta / STEPS;
187 }
188
189 ThumbCreator::Flags MltPreview::flags() const
190 {
191     return None;
192 }
193
194
195 #include "westleypreview.moc"
196